Compare commits

...

60 Commits

Author SHA1 Message Date
Elio Struyf
36ae7081d1 Merge pull request #723 from estruyf/dev
Version 9.4.0
2023-12-12 16:29:45 +01:00
Elio Struyf
7e4c5b0469 Merge branch 'main' into dev 2023-12-12 16:24:43 +01:00
Elio Struyf
5fcd8d6fe7 Enhancement: In data, provide extra space between objects #719 2023-12-12 09:30:07 +01:00
Elio Struyf
0f07be3e3b Issue: DataFileHelper::process: Function yaml.safeLoad is removed in js-yaml 4. #717 2023-12-08 10:26:42 +01:00
Elio Struyf
5444925cf4 Fix ct - boolean field 2023-12-08 09:33:15 +01:00
Elio Struyf
2b89dda08c Added release notes 2023-12-07 17:41:53 +01:00
Elio Struyf
c4feb205fc New localization files 2023-12-07 16:12:21 +01:00
Elio Struyf
78bfa62ed4 #722 - sub-content support 2023-12-07 11:04:18 +01:00
Elio Struyf
f350d4af91 #718 - Fix JSON schema 2023-12-01 10:34:26 +01:00
Elio Struyf
d15f2554a1 Add ability to always use quotes around string
values in front matter #713
2023-11-23 15:24:40 +01:00
Elio Struyf
ab603d9419 Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2023-11-22 10:44:32 +01:00
Elio Struyf
1acf85c6da Fix taxonomy filtering from taxonomy view to content view #714 2023-11-22 10:44:11 +01:00
Elio Struyf
3383a770b4 Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2023-11-17 18:44:29 +01:00
Elio Struyf
a6d2fffeb9 Update media 2023-11-17 18:43:35 +01:00
Elio Struyf
3d530f7929 #712 - Add fix for media file deletion search context 2023-11-17 18:42:54 +01:00
Elio Struyf
f02f994174 Update changelog 2023-11-16 15:27:30 +01:00
Elio Struyf
343dfb2d5d #711 - Fix character mapping 2023-11-16 15:26:32 +01:00
Elio Struyf
9ddaedaaf2 #710 - Remove the fields from FM when the when clause is not met 2023-11-15 15:09:42 +01:00
Elio Struyf
32c6b4c5ea Unused reference 2023-11-15 13:34:15 +01:00
Elio Struyf
90f4d92741 Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2023-11-15 10:07:12 +01:00
Elio Struyf
580b1dca6d Array check in taxonomy picker 2023-11-15 10:07:05 +01:00
Elio Struyf
520bdf2be3 #709 - Where clause on content creation 2023-11-15 09:44:22 +01:00
Elio Struyf
9724168c3f Updated docs 2023-11-14 19:46:59 +01:00
Elio Struyf
ab02e64708 Remove pnpm lock 2023-11-14 19:45:38 +01:00
Elio Struyf
f857b0eaa8 #707 - Fix clearempty 2023-11-13 20:29:49 +00:00
Elio Struyf
03aee57221 Fix style of divider 2023-11-13 20:11:34 +00:00
Elio Struyf
07db685666 #706 - Error handling 2023-11-13 17:57:45 +01:00
Elio Struyf
8684f32d98 Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2023-11-13 16:35:22 +01:00
Elio Struyf
2e536a4de7 Update sponsor check 2023-11-13 16:34:58 +01:00
Elio Struyf
739dffe274 Enhancement: Better content type creation for Astro #704 2023-11-11 13:27:07 -08:00
Elio Struyf
41d2f3f22b Enhancement: Better content type creation for Astro #704 2023-11-11 13:10:41 -08:00
Elio Struyf
e285e8d775 Enhancement: Better content type creation for Astro #704 2023-11-11 12:47:41 -08:00
Elio Struyf
9cf4ae46dd Update changelog 2023-11-11 12:36:20 -08:00
Elio Struyf
72c9d1e25f Merge branch 'localization' into dev 2023-11-11 12:35:52 -08:00
Elio Struyf
5f13d318c2 Enhancement: Better content type creation for Astro #704 2023-11-11 12:35:32 -08:00
Elio Struyf
ffdf8262d7 Localization updates 2023-11-11 12:33:55 -08:00
Elio Struyf
7fcbdb4ade Merge branch 'dev' into localization 2023-11-11 11:25:15 -08:00
Elio Struyf
a8407920bc Enhancement: Better content type creation for Astro #704 2023-11-11 11:19:11 -08:00
Elio Struyf
e1f6c90fc0 Enhancement: Better content type creation for Astro #704 2023-11-11 10:38:31 -08:00
Elio Struyf
11354ad8e5 #704 - Fix in zod optional check 2023-11-11 10:12:21 -08:00
Elio Struyf
af3e63046c empty commit 2023-11-09 15:39:38 -08:00
Elio Struyf
cee4621964 Merge branch 'dev' into localization 2023-11-09 14:16:14 -08:00
Elio Struyf
8db76abf51 #703 - fix pnpm support in astro script 2023-11-09 14:15:43 -08:00
Elio Struyf
3ca50bbe58 Remove reference 2023-11-09 13:52:51 -08:00
Elio Struyf
e4cb93274b Localization updates 2023-11-06 14:57:03 -08:00
Elio Struyf
39ad0e034e Localization updates 2023-11-06 13:26:45 -08:00
Elio Struyf
cef607ffc8 #701 - Fix content creation 2023-11-06 15:19:46 +01:00
Elio Struyf
258c5fe670 Updates to localization 2023-11-06 15:11:00 +01:00
Elio Struyf
046b24337d Merge branch 'dev' into localization 2023-11-05 08:55:13 +01:00
Elio Struyf
24fb1347df Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2023-11-02 18:31:53 +01:00
Elio Struyf
3665823803 #700 - added relative path token 2023-11-02 18:31:40 +01:00
Elio Struyf
a020b9ad8c Merge branch 'dev' of github.com:estruyf/vscode-front-matter into dev 2023-11-02 17:23:56 +01:00
Elio Struyf
2c026ff5e5 Started localizing the backend 2023-11-02 17:17:40 +01:00
Elio Struyf
966d0107d9 Enhancement: Allow the contentRelationship field to have a quick search feature #690 2023-11-01 14:44:59 +01:00
Elio Struyf
c770a9035f Merge branch 'combobox' into dev 2023-11-01 14:42:39 +01:00
Elio Struyf
f4816afa65 Merge pull request #698 from estruyf/issue/697 2023-10-27 09:25:26 +02:00
Elio Struyf
6d43ff11a4 9.3.1 2023-10-27 09:20:45 +02:00
Elio Struyf
1c5e98d40c #697 - Fix missing localization key 2023-10-27 09:20:29 +02:00
Elio Struyf
ff1c00167c Merge pull request #682 from estruyf/dev 2023-10-06 17:26:09 +02:00
Elio Struyf
599d43b254 Merge pull request #658 from estruyf/dev 2023-09-11 10:05:26 +02:00
77 changed files with 2329 additions and 8796 deletions

View File

@@ -1,19 +1,29 @@
# Change Log
## [9.4.0] - 2023-xx-xx
## [9.4.0] - 2023-12-12 - [Release notes](https://beta.frontmatter.codes/updates/v9.4.0)
### ✨ New features
- Localization implemented for the whole extension
### 🎨 Enhancements
- [#273](https://github.com/estruyf/vscode-front-matter/issues/273): Allow single value arrays to be set as a string with the `singleValueAsString` field property
- [#686](https://github.com/estruyf/vscode-front-matter/issues/686): Allow script authors to ask questions during script execution
- [#688](https://github.com/estruyf/vscode-front-matter/issues/688): Allow to show the scheduled articles in the content dashboard (filter and group)
- [#690](https://github.com/estruyf/vscode-front-matter/issues/690): Added the ability to filter values in the `contentRelationship` field
- [#700](https://github.com/estruyf/vscode-front-matter/issues/700): Added the `{{pathToken.relPath}}` placeholder for the `previewPath` property
- [#706](https://github.com/estruyf/vscode-front-matter/issues/706): Show the error of scripts failing in the Front Matter output panel
- [#709](https://github.com/estruyf/vscode-front-matter/issues/709): Take "where clause" into account on content creation
- [#710](https://github.com/estruyf/vscode-front-matter/issues/710): Hide child field when parent field its "when clause" is not met, also remove the fields from the content
- [#713](https://github.com/estruyf/vscode-front-matter/issues/713): Add the ability to always use quotes around string values in front matter
- [#722](https://github.com/estruyf/vscode-front-matter/issues/722): Allow to create sub-content which shows a dialog to select the parent folder
### ⚡️ Optimizations
- Dashboard layout grid optimizations
- Added the content-type name to the metadata section in the panel
- New implementation of the combobox for the `contentRelationship` field
### 🐞 Fixes
@@ -22,6 +32,15 @@
- [#694](https://github.com/estruyf/vscode-front-matter/issues/694): Start terminal session from the folder where the `frontmatter.json` file is located
- [#696](https://github.com/estruyf/vscode-front-matter/issues/696): Close the local server terminal on restart
- [#699](https://github.com/estruyf/vscode-front-matter/issues/699): Changing border theme variable for the dashboard header
- [#703](https://github.com/estruyf/vscode-front-matter/issues/703): Fix retrieval of Astro Collections for `pnpm` projects
- [#704](https://github.com/estruyf/vscode-front-matter/issues/704): Fix `zod` schema script for optional fields
- [#707](https://github.com/estruyf/vscode-front-matter/issues/707): Fix `clearEmpty` issue with `draft` and `boolean` fields which are by default set to `true`
- [#711](https://github.com/estruyf/vscode-front-matter/issues/711): Fix in character mapping in the slug field
- [#712](https://github.com/estruyf/vscode-front-matter/issues/712): Keep the search context when deleting media files
- [#714](https://github.com/estruyf/vscode-front-matter/issues/714): Fix for taxonomy filtering from taxonomy view to content view
- [#717](https://github.com/estruyf/vscode-front-matter/issues/717): Fix in loading yaml data files
- [#718](https://github.com/estruyf/vscode-front-matter/issues/718): Fix JSON schema for the `frontMatter.panel.actions.disabled` setting
- [#719](https://github.com/estruyf/vscode-front-matter/issues/719): Fix styling on data view with objects views
## [9.3.1] - 2023-10-27

View File

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

1
l10n/bundle.l10n.es.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -28,6 +28,13 @@
"common.pin": "Pin",
"common.unpin": "Unpin",
"common.noResults": "No results",
"common.error": "Sorry, something went wrong.",
"common.yes": "yes",
"common.no": "no",
"common.openSettings": "Open settings",
"notifications.outputChannel.link": "output window",
"notifications.outputChannel.description": "Check the {0} for more details.",
"settings.view.common": "Common",
"settings.view.contentFolders": "Content folders",
@@ -269,6 +276,7 @@
"dashboard.steps.stepsToGetStarted.showDashboard.description": "Once all actions are completed, the dashboard can be loaded.",
"dashboard.steps.stepsToGetStarted.template.name": "Use a configuration template",
"dashboard.steps.stepsToGetStarted.template.description": "Select a template to prefill the frontmatter.json file with the recommended settings.",
"dashboard.steps.stepsToGetStarted.template.warning": "Selecting a template applies a whole configuration to your project and closes this configuration view.",
"dashboard.steps.stepsToGetStarted.astroContentTypes.name": "Create Content-Types for your Astro Content Collections",
"dashboard.taxonomyView.button.add.title": "Add {0} to taxonomy settings",
@@ -312,7 +320,7 @@
"dashboard.media.detailsSlideOver.unmapped.description": "Do you want to remap the metadata of unmapped files?",
"dashboard.configuration.astro.astroContentTypes.empty": "No Astro Content Collections found.",
"dashboard.configuration.astro.astroContentTypes.description": "The following Astro Content Collections and can be used to generate a content-type.",
"dashboard.configuration.astro.astroContentTypes.description": "The following Astro Content Collections can be used to generate a content-type.",
"panel.contentType.contentTypeValidator.title": "Content-type",
"panel.contentType.contentTypeValidator.hint": "We noticed field differences between the content-type and the front matter data. \n Would you like to create, update, or set the content-type for this content?",
@@ -457,5 +465,250 @@
"panel.viewPanel.mediaInsert": "Continue in the media dashboard to select the image you want to insert.",
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Template files copied."
"commands.article.notification.noTaxonomy": "No {0} configured.",
"commands.article.quickPick.placeholder": "Select your {0} to insert.",
"commands.article.setDate.error": "Something failed while parsing the date format. Check your \"{0}\" setting.",
"commands.article.updateSlug.error": "Failed to rename file: {0}",
"commands.cache.cleared": "Cache cleared",
"commands.chatbot.title": "Ask me anything",
"commands.content.option.contentType.label": "Create content by content type",
"commands.content.option.contentType.description": "Select if you want to create new content by the available content type(s)",
"commands.content.option.template.label": "Create content by template",
"commands.content.option.template.description": "Select if you want to create new content by the available template(s)",
"commands.content.quickPick.title": "Create content",
"commands.content.quickPick.placeholder": "Select how you want to create your new content",
"commands.dashboard.title": "Dashboard",
"commands.folders.addMediaFolder.inputBox.title": "Add media folder",
"commands.folders.addMediaFolder.inputBox.prompt": "Which name would you like to give to your folder (use \"/\" to create multi-level folders)?",
"commands.folders.addMediaFolder.noFolder.warning": "No folder name was specified.",
"commands.folders.create.folderExists.warning": "Folder is already registered",
"commands.folders.create.input.title": "Register folder",
"commands.folders.create.input.prompt": "Which name would you like to specify for this folder?",
"commands.folders.create.input.placeholder": "Folder name",
"commands.folders.create.success": "Folder registered",
"commands.folders.getWorkspaceFolder.workspaceFolderPick.placeholder": "Please select the main workspace folder for Front Matter to use.",
"commands.folders.get.notificationError.title": "Folder \"{0}\" does not exist. Please remove it from the settings.",
"commands.folders.get.notificationError.remove.action": "Remove folder",
"commands.folders.get.notificationError.create.action": "Create folder",
"commands.preview.panel.title": "Preview: {0}",
"commands.preview.askUserToPickFolder.title": "Select the folder of the article to preview",
"commands.project.initialize.success": "Project initialized successfully.",
"commands.project.switchProject.title": "To which project do you want to switch?",
"commands.project.createSampleTemplate.info": "Sample template created.",
"commands.settings.create.input.prompt": "Insert the value of the {0} that you want to add to your configuration.",
"commands.settings.create.input.placeholder": "Name of the {0}",
"commands.settings.create.warning": "The provided {0} already exists.",
"commands.settings.create.quickPick.placeholder": "Do you want to add the new {0} to the page?",
"commands.settings.export.progress.title": "{0}: exporting tags and categories",
"commands.settings.export.progress.success": "Export completed. Tags: {0} - Categories: {1}.",
"commands.settings.remap.quickpick.title": "Remap",
"commands.settings.remap.quickpick.placeholder": "What do you want to remap?",
"commands.settings.remap.noTaxonomy.warning": "No {0} configured.",
"commands.settings.remap.selectTaxonomy.placeholder": "Select your {0} to insert.",
"commands.settings.remap.newOption.input.prompt": "Specify the value of the {0} with which you want to remap \"{1}\". Leave the input <blank> if you want to remove the {0} from all articles.",
"commands.settings.remap.newOption.input.placeholder": "Name of the {0}",
"commands.settings.remap.delete.placeholder": "Delete {0} {1}?",
"commands.statusListener.verifyRequiredFields.diagnostic.emptyField": "The {0} field is required. Please define a value for the field.",
"commands.statusListener.verifyRequiredFields.notification.error": "The following fields are required to contain a value: {0}",
"commands.template.generate.input.title": "Template title",
"commands.template.generate.input.prompt": "Which name would you like to give your template?",
"commands.template.generate.input.placeholder": "article",
"commands.template.generate.noTitle.warning": "You did not specify a template title.",
"commands.template.generate.keepContents.title": "Keep content",
"commands.template.generate.keepContents.placeholder": "Do you want to keep the contents for the template?",
"commands.template.generate.keepContents.noOption.warning": "You did not pick any of the options for keeping the template its content.",
"commands.template.generate.keepContents.success": "Template created and is now available in your {0} folder.",
"commands.template.getTemplates.warning": "No templates found.",
"commands.template.create.folderPath.warning": "Incorrect project folder path retrieved.",
"commands.template.create.noTemplates.warning": "No templates found.",
"commands.template.create.selectTemplate.title": "Select a template",
"commands.template.create.selectTemplate.placeholder": "Select the content template to use",
"commands.template.create.selectTemplate.noTemplate.warning": "No template selected.",
"commands.template.create.selectTemplate.notFound.warning": "Content template could not be found.",
"commands.template.create.success": "Your new content is now available.",
"commands.wysiwyg.command.unorderedList.label": "Unordered list",
"commands.wysiwyg.command.unorderedList.detail": "Add an unordered list",
"commands.wysiwyg.command.orderedList.label": "Ordered list",
"commands.wysiwyg.command.orderedList.detail": "Add an ordered list",
"commands.wysiwyg.command.taskList.label": "Task list",
"commands.wysiwyg.command.taskList.detail": "Add a task list",
"commands.wysiwyg.command.code.label": "Code",
"commands.wysiwyg.command.code.detail": "Add inline code snippet",
"commands.wysiwyg.command.codeblock.label": "Code block",
"commands.wysiwyg.command.codeblock.detail": "Add a code block",
"commands.wysiwyg.command.blockquote.label": "Blockquote",
"commands.wysiwyg.command.blockquote.detail": "Add a blockquote",
"commands.wysiwyg.command.strikethrough.label": "Strikethrough",
"commands.wysiwyg.command.strikethrough.detail": "Add strikethrough text",
"commands.wysiwyg.quickPick.title": "WYSIWYG Options",
"commands.wysiwyg.quickPick.placeholder": "Which type of markup would you like to insert?",
"commands.wysiwyg.addHyperlink.hyperlinkInput.title": "WYSIWYG Hyperlink",
"commands.wysiwyg.addHyperlink.hyperlinkInput.prompt": "Enter the URL",
"commands.wysiwyg.addHyperlink.textInput.title": "WYSIWYG Text",
"commands.wysiwyg.addHyperlink.textInput.prompt": "Enter the text for the hyperlink",
"commands.wysiwyg.insertText.heading.input.title": "Heading level",
"commands.wysiwyg.insertText.heading.input.placeholder": "Which heading level do you want to insert?",
"helpers.articleHelper.createContent.pageBundle.error": "A page bundle with the name {0} already exists in {1}.",
"helpers.articleHelper.createContent.contentExists.warning": "Content with the title already exists. Please specify a new title.",
"helpers.articleHelper.processCustomPlaceholders.placeholder.error": "Error while processing the {0} placeholder.",
"helpers.articleHelper.parseFile.diagnostic.error": "Error parsing the front matter of {0}.",
"helpers.contentType.generate.noFrontMatter.error": "No front matter data found to generate a content type.",
"helpers.contentType.generate.override.quickPick.title": "Override the default content type",
"helpers.contentType.generate.override.quickPick.placeholder": "Do you want to overwrite the default content type configuration with the fields used in the current field?",
"helpers.contentType.generate.contentTypeInput.title": "Generate Content Type",
"helpers.contentType.generate.contentTypeInput.prompt": "Enter the name of the content type to generate",
"helpers.contentType.generate.contentTypeInput.validation.enterName": "Please enter a name for the content type.",
"helpers.contentType.generate.contentTypeInput.validation.nameExists": "A content type with this name already exists.",
"helpers.contentType.generate.noContentTypeName.warning": "You didn't specify a name for the content type.",
"helpers.contentType.generate.pageBundle.quickPick.title": "Use as a page bundle",
"helpers.contentType.generate.pageBundle.quickPick.placeHolder": "Do you want to use this content type as a page bundle?",
"helpers.contentType.generate.updated.success": "Content type {0} has been updated.",
"helpers.contentType.generate.generated.success": "Content type {0} has been generated.",
"helpers.contentType.addMissingFields.noFrontMatter.warning": "No front matter data found to add missing fields.",
"helpers.contentType.addMissingFields.updated.success": "Content type {0} has been updated.",
"helpers.contentType.setContentType.noFrontMatter.warning": "No front matter data found to set the content type.",
"helpers.contentType.setContentType.quickPick.title": "Select the content type",
"helpers.contentType.setContentType.quickPick.placeholder": "Which content type would you like to use?",
"helpers.contentType.create.allowSubContent.title": "Do you want to create it as sub-content?",
"helpers.contentType.create.allowSubContent.placeHolder": "Do you want to create it as sub-content?",
"helpers.contentType.create.allowSubContent.showOpenDialog.openLabel": "Select folder",
"helpers.contentType.create.allowSubContent.showOpenDialog.title": "Select folder to create the content",
"helpers.contentType.create.pageBundle.title": "Create as a page bundle?",
"helpers.contentType.create.pageBundle.placeHolder": "Do you want to create the sub-content as a page bundle?",
"helpers.contentType.create.progress.title": "{0}: Creating content...",
"helpers.contentType.create.success": "Your new content has been created.",
"helpers.contentType.verify.warning": "The content type actions are not available in this mode.",
"helpers.customScript.executing": "Executing: {0}",
"helpers.customScript.singleRun.article.warning": "{0}: Article couldn't be retrieved.",
"helpers.customScript.bulkRun.noFiles.warning": "{0}: No files found",
"helpers.customScript.runMediaScript.noFolder.warning": "{0}: There was no folder or media path specified.",
"helpers.customScript.showOutput.frontMatter.success": "{0}: front matter updated.",
"helpers.customScript.showOutput.copyOutput.action": "Copy output",
"helpers.customScript.showOutput.success": "{0}: Executed your custom script.",
"helpers.customScript.validateCommand.error": "Invalid command: {0}",
"helpers.dataFileHelper.process.error": "Something went wrong while processing the data file.",
"helpers.extension.getVersion.changelog": "Check the changelog",
"helpers.extension.getVersion.starIt": "Give it a ⭐️",
"helpers.extension.getVersion.update.notification": "{0} has been updated to v{1} — check out what's new!",
"helpers.extension.migrateSettings.deprecated.warning": "The \"{0}\" and \"{1}\" settings have been deprecated. Please use the \"isPublishDate\" and \"isModifiedDate\" datetime field properties instead.",
"helpers.extension.migrateSettings.deprecated.warning.hide": "Hide",
"helpers.extension.migrateSettings.deprecated.warning.seeGuide": "See migration guide",
"helpers.extension.migrateSettings.templates.quickPick.title": "{0} - Templates",
"helpers.extension.migrateSettings.templates.quickPick.placeholder": "Do you want to keep on using the template functionality?",
"helpers.extension.checkIfExtensionCanRun.warning": "Front Matter BETA cannot be used while the stable version is installed. Please ensure that you have only over version installed.",
"helpers.mediaHelper.saveFile.folder.error": "We couldn't find your selected folder.",
"helpers.mediaHelper.saveFile.file.uploaded.success": "File {0} uploaded to: {1}",
"helpers.mediaHelper.saveFile.file.uploaded.failed": "Sorry, something went wrong uploading {0}",
"helpers.mediaHelper.deleteFile.file.deletion.failed": "Sorry, something went wrong deleting {0}",
"helpers.mediaLibrary.remove.warning": "The name \"{0}\" already exists at the file location.",
"helpers.mediaLibrary.remove.error": "Sorry, something went wrong updating \"{0}\" to \"{1}\".",
"helpers.openFileInEditor.error": "Couldn't open the file.",
"helpers.questions.contentTitle.aiInput.title": "Title or description",
"helpers.questions.contentTitle.aiInput.prompt": "What would you like to write about?",
"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.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.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?",
"helpers.questions.contentTitle.titleInput.placeholder": "Content title",
"helpers.questions.contentTitle.titleInput.warning": "You did not specify a title for your content.",
"helpers.questions.selectContentFolder.quickPick.title": "Select a folder",
"helpers.questions.selectContentFolder.quickPick.placeholder": "Select where you want to create your content",
"helpers.questions.selectContentFolder.quickPick.noFolders.warning": "No page folders were configured.",
"helpers.questions.selectContentFolder.quickPick.noSelection.warning": "You didn't select a place where you wanted to create your content.",
"helpers.questions.selectContentType.noContentType.warning": "No content types found. Please create a content type first.",
"helpers.questions.selectContentType.quickPick.title": "Content type",
"helpers.questions.selectContentType.quickPick.placeholder": "Select the content type to create your new content",
"helpers.questions.selectContentType.noSelection.warning": "No content type was selected.",
"helpers.seoHelper.checkLength.diagnostic.message": "Article {0} is longer than {1} characters (current length: {2}). For SEO reasons, it would be better to make it less than {1} characters.",
"helpers.settingsHelper.checkToPromote.message": "You have local settings. Would you like to promote them to the global settings (\"frontmatter.json\")?",
"helpers.settingsHelper.promote.success": "All settings promoted to team level.",
"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.taxonomyHelper.rename.input.title": "Rename the {0}",
"helpers.taxonomyHelper.rename.validate.equalValue": "The new value must be different from the old one.",
"helpers.taxonomyHelper.rename.validate.noValue": "A new value must be provided.",
"helpers.taxonomyHelper.merge.quickPick.title": "Merge the \"{0}\" with another {1} value",
"helpers.taxonomyHelper.merge.quickPick.placeholder": "Select the {0} value to merge with",
"helpers.taxonomyHelper.delete.quickPick.title": "Delete the \"{0}\" {1} value",
"helpers.taxonomyHelper.delete.quickPick.placeholder": "Are you sure you want to delete the \"{0}\" {1} value?",
"helpers.taxonomyHelper.createNew.input.title": "Create a new {0} value",
"helpers.taxonomyHelper.createNew.input.placeholder": "Enter the value you want to add",
"helpers.taxonomyHelper.createNew.input.validate.noValue": "A value must be provided.",
"helpers.taxonomyHelper.createNew.input.validate.exists": "The value already exists.",
"helpers.taxonomyHelper.process.edit": "{0}: Renaming \"{1}\" from {2} to {3}.",
"helpers.taxonomyHelper.process.merge": "{0}: Merging \"{1}\" from {2} to {3}.",
"helpers.taxonomyHelper.process.delete": "{0}: Deleting \"{1}\" from {2}.",
"helpers.taxonomyHelper.process.edit.success": "Edit completed.",
"helpers.taxonomyHelper.process.merge.success": "Merge completed.",
"helpers.taxonomyHelper.process.delete.success": "Deletion completed.",
"helpers.taxonomyHelper.move.quickPick.title": "Move the \"{0}\" to another type",
"helpers.taxonomyHelper.move.quickPick.placeholder": "Select the type to move to",
"helpers.taxonomyHelper.move.progress.title": "{0}: Moving \"{1}\" from {2} to \"${3}\".",
"helpers.taxonomyHelper.move.success": "Move completed.",
"listeners.dashboard.dashboardListener.openConfig.notification": "Open the \"frontmatter.json\" file if you want to review the configuration.",
"listeners.dashboard.dashboardListener.pinItem.noPath.error": "No path provided.",
"listeners.dashboard.dashboardListener.pinItem.coundNotPin.error": "Could not pin item.",
"listeners.dashboard.dashboardListener.pinItem.coundNotUnPin.error": "Could not unpin item.",
"listeners.dashboard.settingsListener.triggerTemplate.notification": "Template files copied.",
"listeners.dashboard.settingsListener.triggerTemplate.progress.title": "Downloading and initializing the template...",
"listeners.dashboard.settingsListener.triggerTemplate.download.error": "Failed to download the template.",
"listeners.dashboard.settingsListener.triggerTemplate.init.error": "Failed to initialize the template.",
"listeners.dashboard.snippetListener.addSnippet.missingFields.warning": "Snippet missing title or body",
"listeners.dashboard.snippetListener.addSnippet.exists.warning": "Snippet with the same title already exists",
"listeners.dashboard.snippetListener.updateSnippet.noSnippets.warning": "No snippets to update",
"listeners.general.gitListener.push.error": "Failed to push submodules.",
"listeners.panel.dataListener.aiSuggestTaxonomy.noEditor.error": "No active editor",
"listeners.panel.dataListener.aiSuggestTaxonomy.noData.error": "No article data",
"listeners.panel.dataListener.getDataFileEntries.noDataFiles.error": "Couldn't find data file entries",
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noEditor.error": "No active editor",
"listeners.panel.taxonomyListener.aiSuggestTaxonomy.noData.error": "No article data",
"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}",
"services.pagesParser.parsePages.statusBar.text": "Processing...",
"services.pagesParser.parsePages.file.error": "File error: {0}",
"services.sponsorAi.getTitles.warning": "The AI title generation took too long. Please try again later.",
"services.sponsorAi.getDescription.warning": "The AI description generation took too long. Please try again later.",
"services.sponsorAi.getTaxonomySuggestions.warning": "The AI taxonomy generation took too long. Please try again later.",
"services.terminal.openLocalServerTerminal.terminalOption.message": "Starting local server"
}

1
l10n/bundle.l10n.ko.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -1005,15 +1005,18 @@
"type": "array",
"default": [],
"markdownDescription": "%setting.frontMatter.panel.actions.disabled.markdownDescription%",
"enum": [
"openDashboard",
"createContent",
"optimizeSlug",
"preview",
"openOnWebsite",
"startStopServer",
"customActions"
]
"items": {
"type": "string",
"enum": [
"openDashboard",
"createContent",
"optimizeSlug",
"preview",
"openOnWebsite",
"startStopServer",
"customActions"
]
}
},
"frontMatter.preview.host": {
"type": "string",
@@ -1082,7 +1085,17 @@
"clearEmpty": {
"type": "boolean",
"default": false,
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.clearEmpty.description%"
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.clearEmpty.description%"
},
"allowAsSubContent": {
"type": "boolean",
"default": false,
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.allowAsSubContent.description%"
},
"isSubContent": {
"type": "boolean",
"default": false,
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description%"
},
"fields": {
"$id": "#contenttypefield",
@@ -1743,6 +1756,12 @@
"markdownDescription": "%setting.frontMatter.taxonomy.modifiedField.markdownDescription%",
"deprecationMessage": "%setting.frontMatter.taxonomy.modifiedField.deprecationMessage%"
},
"frontMatter.taxonomy.quoteStringValues": {
"type": "boolean",
"default": false,
"markdownDescription": "%setting.frontMatter.taxonomy.quoteStringValues.markdownDescription%",
"scope": "Taxonomy"
},
"frontMatter.taxonomy.noPropertyValueQuotes": {
"type": "array",
"default": [],
@@ -2514,7 +2533,7 @@
"clean": "rimraf dist",
"start:site": "cd ./docs && npm run dev",
"clean:test": "rm ./e2e/sample/frontmatter.json || exit 0 && rm -rf ./e2e/sample/.frontmatter || exit 0",
"test": "pnpm lint; tsc -p tsconfig.e2e.json && npm run clean:test && pnpm i -g @vscode/vsce && node ./e2e/out/runTests.js",
"test": "npm run lint; tsc -p tsconfig.e2e.json && npm run clean:test && npm run i -g @vscode/vsce && node ./e2e/out/runTests.js",
"lint": "eslint --max-warnings=0 ./src/{commands,components}",
"prettier": "prettier --write ./src",
"localization:watch": "node ./scripts/watch-localization.js",

View File

@@ -225,6 +225,7 @@
"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.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)",
@@ -244,8 +245,10 @@
"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.fields.items.properties.clearEmpty.description": "Specify if the empty values should be cleared.",
"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)",
"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). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.config.dynamicfilepath)",
"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."
}

8237
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
const fs = require('fs');
const path = require('path');
const jsoncParser = require('jsonc-parser');
const camlCase = (str) => {
const words = str.split('.');
@@ -15,12 +16,14 @@ const camlCase = (str) => {
const enFile = fs.readFileSync(path.join(__dirname, '../l10n/bundle.l10n.json'), 'utf8');
// Parse the EN file
const en = JSON.parse(enFile);
const en = jsoncParser.parse(enFile);
const keys = Object.keys(en);
// Create an enum file
const enumFile = fs.createWriteStream(path.join(__dirname, '../src/localization/localization.enum.ts'));
const enumFile = fs.createWriteStream(
path.join(__dirname, '../src/localization/localization.enum.ts')
);
// Write the enum file header
enumFile.write(`export enum LocalizationKey {\n`);
@@ -38,4 +41,4 @@ const camlCase = (str) => {
// Close the enum file
enumFile.close();
})();
})();

View File

@@ -29,6 +29,8 @@ import { NavigationType } from '../dashboardWebView/models';
import { processKnownPlaceholders } from '../helpers/PlaceholderHelper';
import { Position } from 'vscode';
import { SNIPPET } from '../constants/Snippet';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Article {
/**
@@ -80,12 +82,20 @@ export class Article {
}
if (options.length === 0) {
Notifications.info(`No ${type === TaxonomyType.Tag ? 'tags' : 'categories'} configured.`);
Notifications.info(
l10n.t(
LocalizationKey.commandsArticleNotificationNoTaxonomy,
type === TaxonomyType.Tag ? 'tags' : 'categories'
)
);
return;
}
const selectedOptions = await vscode.window.showQuickPick(options, {
placeHolder: `Select your ${type === TaxonomyType.Tag ? 'tags' : 'categories'} to insert`,
placeHolder: l10n.t(
LocalizationKey.commandsArticleQuickPickPlaceholder,
type === TaxonomyType.Tag ? 'tags' : 'categories'
),
canPickMany: true,
ignoreFocusOut: true
});
@@ -117,7 +127,7 @@ export class Article {
ArticleHelper.update(editor, article);
} catch (e) {
Notifications.error(
`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`
l10n.t(LocalizationKey.commandsArticleSetDateError, `${CONFIG_KEY}${SETTING_DATE_FORMAT}`)
);
}
}
@@ -180,7 +190,7 @@ export class Article {
return cloneArticle;
} catch (e: unknown) {
Notifications.error(
`Something failed while parsing the date format. Check your "${CONFIG_KEY}${SETTING_DATE_FORMAT}" setting.`
l10n.t(LocalizationKey.commandsArticleSetDateError, `${CONFIG_KEY}${SETTING_DATE_FORMAT}`)
);
}
}
@@ -292,7 +302,12 @@ export class Article {
overwrite: false
});
} catch (e: unknown) {
Notifications.error(`Failed to rename file: ${(e as Error).message || e}`);
Notifications.error(
l10n.t(
LocalizationKey.commandsArticleUpdateSlugError,
((e as Error).message || e) as string
)
);
}
}
}

View File

@@ -1,77 +1,65 @@
import { commands, ExtensionContext } from 'vscode';
import { authentication, commands, ExtensionContext } from 'vscode';
import { COMMAND_NAME, CONTEXT } from '../constants';
import { Extension } from '../helpers';
import { Credentials } from '../services/Credentials';
import { Extension, Logger } from '../helpers';
import fetch from 'node-fetch';
import { Dashboard } from './Dashboard';
import { SettingsListener } from '../listeners/panel';
import { PanelProvider } from '../panelWebView/PanelProvider';
export class Backers {
private static creds: Credentials | null = null;
public static async init(context: ExtensionContext) {
Backers.creds = new Credentials();
await Backers.creds.initialize(context, Backers.tryUsernameCheck);
Backers.tryUsernameCheck();
Backers.checkSponsor();
context.subscriptions.push(
commands.registerCommand(COMMAND_NAME.authenticate, async () => {
Backers.tryUsernameCheck();
await authentication.getSession('github', ['read:user'], { createIfNone: true });
Backers.checkSponsor();
})
);
}
public static async tryUsernameCheck() {
try {
const username = await Backers.getUsername();
Backers.validate(username || '');
} catch (e) {
Backers.validate('');
}
}
public static async getUsername() {
const octokit = await Backers.creds?.getOctokit();
const user = await octokit?.users.getAuthenticated();
if (user?.data?.login) {
return user?.data?.login;
}
return;
}
public static async validate(username: string) {
public static async checkSponsor() {
const ext = Extension.getInstance();
const githubAuth = await authentication.getSession('github', ['read:user'], { silent: true });
if (githubAuth && githubAuth.accessToken) {
try {
const isBeta = ext.isBetaVersion();
const response = await fetch(
`https://${isBeta ? `beta.` : ``}frontmatter.codes/api/v2/backers`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
accept: 'application/json'
},
body: JSON.stringify({
token: githubAuth.accessToken
})
}
);
if (!username) {
ext.setState(CONTEXT.backer, undefined, 'global');
}
if (response.ok) {
const prevData = await ext.getState<boolean>(CONTEXT.backer, 'global');
await ext.setState(CONTEXT.backer, true, 'global');
const isBeta = ext.isBetaVersion();
if (!prevData) {
const PanelView = PanelProvider.getInstance();
if (PanelView.visible) {
SettingsListener.getSettings();
}
const response = await fetch(
`https://${isBeta ? `beta.` : ``}frontmatter.codes/api/backers?backer=${username}`
);
if (response.ok) {
const prevData = await ext.getState<boolean>(CONTEXT.backer, 'global');
await ext.setState(CONTEXT.backer, true, 'global');
if (!prevData) {
const PanelView = PanelProvider.getInstance();
if (PanelView.visible) {
SettingsListener.getSettings();
}
if (Dashboard.isOpen) {
Dashboard.reload();
if (Dashboard.isOpen) {
Dashboard.reload();
}
}
} else {
ext.setState(CONTEXT.backer, false, 'global');
}
} catch (e) {
Logger.error(`Failed to check if user is a sponsor: ${(e as Error).message}`);
}
} else {
ext.setState(CONTEXT.backer, false, 'global');
ext.setState(CONTEXT.backer, undefined, 'global');
}
}
}

View File

@@ -1,6 +1,8 @@
import { commands } from 'vscode';
import { COMMAND_NAME, ExtensionState } from '../constants';
import { Extension, Logger, Notifications } from '../helpers';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Cache {
public static async registerCommands() {
@@ -28,9 +30,9 @@ export class Cache {
await ext.setState(ExtensionState.Settings.Extends, undefined, 'workspace', true);
if (showNotification) {
Notifications.info('Cache cleared');
Notifications.info(l10n.t(LocalizationKey.commandsCacheCleared));
} else {
Logger.info('Cache cleared');
Logger.info(l10n.t(LocalizationKey.commandsCacheCleared));
}
}
}

View File

@@ -2,9 +2,11 @@ import { Telemetry } from './../helpers/Telemetry';
import { TelemetryEvent, PreviewCommands, GeneralCommands } from './../constants';
import { join } from 'path';
import { commands, Uri, ViewColumn, window } from 'vscode';
import { Extension, Settings } from '../helpers';
import { Extension } from '../helpers';
import { WebviewHelper } from '@estruyf/vscode';
import { getLocalizationFile } from '../utils/getLocalizationFile';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Chatbot {
/**
@@ -14,7 +16,7 @@ export class Chatbot {
// Create the preview webview
const webView = window.createWebviewPanel(
'frontMatterChatbot',
'Front Matter AI - Ask me anything',
`Front Matter AI - ${l10n.t(LocalizationKey.commandsChatbotTitle)}`,
{
viewColumn: ViewColumn.Beside,
preserveFocus: true

View File

@@ -1,6 +1,8 @@
import { commands, QuickPickItem, window } from 'vscode';
import { COMMAND_NAME, SETTING_TEMPLATES_ENABLED } from '../constants';
import { Settings } from '../helpers';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Content {
public static async create() {
@@ -12,18 +14,18 @@ export class Content {
const options: QuickPickItem[] = [
{
label: 'Create content by content type',
description: 'Select if you want to create new content by the available content type(s)'
label: l10n.t(LocalizationKey.commandsContentOptionContentTypeLabel),
description: l10n.t(LocalizationKey.commandsContentOptionContentTypeDescription)
},
{
label: 'Create content by template',
description: 'Select if you want to create new content by the available template(s)'
label: l10n.t(LocalizationKey.commandsContentOptionTemplateLabel),
description: l10n.t(LocalizationKey.commandsContentOptionTemplateDescription)
} as QuickPickItem
];
const selectedOption = await window.showQuickPick(options, {
title: 'Create content',
placeHolder: `Select how you want to create your new content`,
title: l10n.t(LocalizationKey.commandsContentQuickPickTitle),
placeHolder: l10n.t(LocalizationKey.commandsContentQuickPickPlaceholder),
canPickMany: false,
ignoreFocusOut: true
});

View File

@@ -29,6 +29,8 @@ import {
import { MediaListener as PanelMediaListener } from '../listeners/panel';
import { GitListener, ModeListener } from '../listeners/general';
import { Folders } from './Folders';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Dashboard {
private static webview: WebviewPanel | null = null;
@@ -119,7 +121,7 @@ export class Dashboard {
// Create the preview webview
Dashboard.webview = window.createWebviewPanel(
'frontMatterDashboard',
'FrontMatter Dashboard',
`Front Matter ${l10n.t(LocalizationKey.commandsDashboardTitle)}`,
ViewColumn.One,
{
enableScripts: true,

View File

@@ -25,6 +25,8 @@ import { Telemetry } from '../helpers/Telemetry';
import { glob } from 'glob';
import { mkdirAsync } from '../utils/mkdirAsync';
import { existsAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
@@ -57,15 +59,15 @@ export class Folders {
}
const folderName = await window.showInputBox({
title: `Add media folder`,
prompt: `Which name would you like to give to your folder (use "/" to create multi-level folders)?`,
title: l10n.t(LocalizationKey.commandsFoldersAddMediaFolderInputBoxTitle),
prompt: l10n.t(LocalizationKey.commandsFoldersAddMediaFolderInputBoxPrompt),
value: startPath,
ignoreFocusOut: true,
placeHolder: `${format(new Date(), `yyyy/MM`)}`
});
if (!folderName) {
Notifications.warning(`No folder name was specified.`);
Notifications.warning(l10n.t(LocalizationKey.commandsFoldersAddMediaFolderNoFolderWarning));
return;
}
@@ -126,15 +128,15 @@ export class Folders {
);
if (exists) {
Notifications.warning(`Folder is already registered`);
Notifications.warning(l10n.t(LocalizationKey.commandsFoldersCreateFolderExistsWarning));
return;
}
if (!folderName) {
folderName = await window.showInputBox({
title: `Register folder`,
prompt: `Which name would you like to specify for this folder?`,
placeHolder: `Folder name`,
title: l10n.t(LocalizationKey.commandsFoldersCreateInputTitle),
prompt: l10n.t(LocalizationKey.commandsFoldersCreateInputPrompt),
placeHolder: l10n.t(LocalizationKey.commandsFoldersCreateInputPlaceholder),
value: basename(folder.fsPath),
ignoreFocusOut: true
});
@@ -154,7 +156,7 @@ export class Folders {
folders = uniqBy(folders, (f) => f.path);
await Folders.update(folders);
Notifications.info(`Folder registered`);
Notifications.info(l10n.t(LocalizationKey.commandsFoldersCreateSuccess));
Telemetry.send(TelemetryEvent.registerFolder);
@@ -245,7 +247,9 @@ export class Folders {
if (!projectFolder) {
window
.showWorkspaceFolderPick({
placeHolder: `Please select the main workspace folder for Front Matter to use.`
placeHolder: l10n.t(
LocalizationKey.commandsFoldersGetWorkspaceFolderWorkspaceFolderPickPlaceholder
)
})
.then(async (selectedFolder) => {
if (selectedFolder) {
@@ -378,14 +382,21 @@ export class Folders {
} else {
if (folderPath && !existsSync(folderPath)) {
Notifications.errorShowOnce(
`Folder "${folder.title} (${folder.path})" does not exist. Please remove it from the settings.`,
'Remove folder',
'Create folder'
l10n.t(
LocalizationKey.commandsFoldersGetNotificationErrorTitle,
`${folder.title} (${folder.path})`
),
l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorRemoveAction),
l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorCreateAction)
).then((answer) => {
if (answer === 'Remove folder') {
if (
answer === l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorRemoveAction)
) {
const folders = Folders.get();
Folders.update(folders.filter((f) => f.path !== folder.path));
} else if (answer === 'Create folder') {
} else if (
answer === l10n.t(LocalizationKey.commandsFoldersGetNotificationErrorCreateAction)
) {
mkdirAsync(folderPath as string, { recursive: true });
}
});

View File

@@ -24,6 +24,8 @@ 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';
export class Preview {
public static filePath: string | undefined = undefined;
@@ -71,7 +73,9 @@ export class Preview {
// Create the preview webview
const webView = window.createWebviewPanel(
'frontMatterPreview',
article?.data?.title ? `Preview: ${article?.data?.title}` : 'FrontMatter Preview',
article?.data?.title
? l10n.t(LocalizationKey.commandsPreviewPanelTitle, article?.data.title)
: 'Front Matter Preview',
{
viewColumn: ViewColumn.Beside,
preserveFocus: true
@@ -393,7 +397,7 @@ export class Preview {
const folderNames = crntFolders.map((folder) => folder.title);
const selectedFolderName = await window.showQuickPick(folderNames, {
canPickMany: false,
title: 'Select the folder of the article to preview'
title: l10n.t(LocalizationKey.commandsPreviewAskUserToPickFolderTitle)
});
if (selectedFolderName) {

View File

@@ -21,6 +21,8 @@ import {
} from '../constants';
import { SettingsListener } from '../listeners/dashboard';
import { existsAsync, writeFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Project {
private static content = `---
@@ -66,7 +68,7 @@ categories: []
if (sampleTemplate !== undefined) {
await Project.createSampleTemplate();
} else {
Notifications.info('Project initialized successfully.');
Notifications.info(l10n.t(LocalizationKey.commandsProjectInitializeSuccess));
}
// Initialize the media library
@@ -89,10 +91,14 @@ categories: []
} catch (error: unknown) {
const err = error as Error;
Logger.error(`Project::init: ${err?.message || err}`);
Notifications.error(`Sorry, something went wrong - ${err?.message || err}`);
Notifications.errorWithOutput(l10n.t(LocalizationKey.commonError));
}
}
/**
* Project switcher
* @returns
*/
public static async switchProject() {
const projects = Settings.getProjects();
const project = await window.showQuickPick(
@@ -100,7 +106,7 @@ categories: []
{
canPickMany: false,
ignoreFocusOut: true,
title: 'Select a project to switch to'
title: l10n.t(LocalizationKey.commandsProjectSwitchProjectTitle)
}
);
@@ -136,7 +142,7 @@ categories: []
await writeFileAsync(article.fsPath, Project.content, {
encoding: 'utf-8'
});
Notifications.info('Sample template created.');
Notifications.info(l10n.t(LocalizationKey.commandsProjectCreateSampleTemplateInfo));
}
}

View File

@@ -5,6 +5,8 @@ import { EXTENSION_NAME } from '../constants';
import { ArticleHelper, 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 {
/**
@@ -13,11 +15,11 @@ export class Settings {
* @param type
*/
public static async create(type: TaxonomyType) {
const taxonomy = type === TaxonomyType.Tag ? 'tag' : 'category';
const newOption = await vscode.window.showInputBox({
prompt: `Insert the value of the ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} that you want to add to your configuration.`,
placeHolder: `Name of the ${type === TaxonomyType.Tag ? 'tag' : 'category'}`,
prompt: l10n.t(LocalizationKey.commandsFoldersCreateInputPrompt, taxonomy),
placeHolder: l10n.t(LocalizationKey.commandsFoldersCreateInputPlaceholder, taxonomy),
ignoreFocusOut: true
});
@@ -25,9 +27,7 @@ export class Settings {
let options = (await TaxonomyHelper.get(type)) || [];
if (options.find((o) => o === newOption)) {
Notifications.info(
`The provided ${type === TaxonomyType.Tag ? 'tag' : 'category'} already exists.`
);
Notifications.warning(l10n.t(LocalizationKey.commandsSettingsCreateWarning, taxonomy));
return;
}
@@ -35,15 +35,16 @@ export class Settings {
TaxonomyHelper.update(type, options);
// Ask if the new term needs to be added to the page
const addToPage = await vscode.window.showQuickPick(['yes', 'no'], {
canPickMany: false,
placeHolder: `Do you want to add the new ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} to the page?`,
ignoreFocusOut: true
});
const addToPage = await vscode.window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
canPickMany: false,
placeHolder: l10n.t(LocalizationKey.commandsSettingsCreateQuickPickPlaceholder, taxonomy),
ignoreFocusOut: true
}
);
if (addToPage && addToPage === 'yes') {
if (addToPage && addToPage === l10n.t(LocalizationKey.commonYes)) {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
@@ -54,7 +55,7 @@ export class Settings {
return;
}
const matterProp: string = type === TaxonomyType.Tag ? 'tags' : 'categories';
const matterProp: string = taxonomy;
// Add the selected options to the options array
if (article.data[matterProp]) {
const propData: string[] = article.data[matterProp];
@@ -83,7 +84,7 @@ export class Settings {
vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: `${EXTENSION_NAME}: exporting tags and categories`,
title: l10n.t(LocalizationKey.commandsSettingsExportProgressTitle, EXTENSION_NAME),
cancellable: false
},
async (progress) => {
@@ -146,7 +147,11 @@ export class Settings {
// Done
Notifications.info(
`Export completed. Tags: ${crntTags.length} - Categories: ${crntCategories.length}.`
l10n.t(
LocalizationKey.commandsSettingsExportProgressSuccess,
crntTags.length,
crntCategories.length
)
);
}
);
@@ -157,8 +162,8 @@ export class Settings {
*/
public static async remap() {
const taxType = await vscode.window.showQuickPick(['Tag', 'Category'], {
title: `Remap`,
placeHolder: `What do you want to remap?`,
title: l10n.t(LocalizationKey.commandsSettingsRemapQuickpickTitle),
placeHolder: l10n.t(LocalizationKey.commandsSettingsRemapQuickpickPlaceholder),
canPickMany: false,
ignoreFocusOut: true
});
@@ -168,15 +173,18 @@ export class Settings {
}
const type = taxType === 'Tag' ? TaxonomyType.Tag : TaxonomyType.Category;
const taxonomy = type === TaxonomyType.Tag ? 'tags' : 'categories';
const options = (await TaxonomyHelper.get(type)) || [];
if (!options || options.length === 0) {
Notifications.info(`No ${type === TaxonomyType.Tag ? 'tags' : 'categories'} configured.`);
Notifications.warning(
l10n.t(LocalizationKey.commandsSettingsRemapNoTaxonomyWarning, taxonomy)
);
return;
}
const selectedOption = await vscode.window.showQuickPick(options, {
placeHolder: `Select your ${type === TaxonomyType.Tag ? 'tags' : 'categories'} to insert`,
placeHolder: l10n.t(LocalizationKey.commandsSettingsRemapSelectTaxonomyPlaceholder, taxonomy),
canPickMany: false,
ignoreFocusOut: true
});
@@ -186,19 +194,23 @@ export class Settings {
}
const newOptionValue = await vscode.window.showInputBox({
prompt: `Specify the value of the ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} with which you want to remap "${selectedOption}". Leave the input <blank> if you want to remove the ${
type === TaxonomyType.Tag ? 'tag' : 'category'
} from all articles.`,
placeHolder: `Name of the ${type === TaxonomyType.Tag ? 'tag' : 'category'}`,
prompt: l10n.t(
LocalizationKey.commandsSettingsRemapNewOptionInputPrompt,
taxonomy,
selectedOption
),
placeHolder: l10n.t(LocalizationKey.commandsSettingsRemapNewOptionInputPlaceholder, taxonomy),
ignoreFocusOut: true
});
if (!newOptionValue) {
const deleteAnswer = await vscode.window.showQuickPick(['yes', 'no'], {
canPickMany: false,
placeHolder: `Delete ${selectedOption} ${type === TaxonomyType.Tag ? 'tag' : 'category'}?`,
placeHolder: l10n.t(
LocalizationKey.commandsSettingsRemapDeletePlaceholder,
selectedOption,
taxonomy
),
ignoreFocusOut: true
});
if (deleteAnswer === 'no') {

View File

@@ -1,6 +1,7 @@
import { ParsedFrontMatter } from './../parsers/FrontMatterParser';
import {
CONTEXT,
EXTENSION_NAME,
NOTIFICATION_TYPE,
SETTING_SEO_DESCRIPTION_FIELD,
SETTING_SEO_DESCRIPTION_LENGTH,
@@ -16,6 +17,8 @@ import { DataListener } from '../listeners/panel';
import { commands } from 'vscode';
import { Field } from '../models';
import { Preview } from './Preview';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class StatusListener {
/**
@@ -135,12 +138,13 @@ export class StatusListener {
const diagnostic: vscode.Diagnostic = {
code: '',
message: `This ${fields
.map((f) => f.name)
.join('/')} field is required to contain a value.`,
message: l10n.t(
LocalizationKey.commandsStatusListenerVerifyRequiredFieldsDiagnosticEmptyField,
fields.map((f) => f.name).join('/')
),
range: new vscode.Range(posStart, posEnd),
severity: vscode.DiagnosticSeverity.Error,
source: 'Front Matter'
source: EXTENSION_NAME
};
requiredDiagnostics.push(diagnostic);
@@ -158,7 +162,10 @@ export class StatusListener {
Notifications.showIfNotDisabled(
NOTIFICATION_TYPE.requiredFieldValidation,
'ERROR_ONCE',
`The following fields are required to contain a value: ${fieldsToReport.join(', ')}`
l10n.t(
LocalizationKey.commandsStatusListenerVerifyRequiredFieldsNotificationError,
fieldsToReport.join(', ')
)
);
}
}

View File

@@ -16,6 +16,8 @@ import { PagesListener } from '../listeners/dashboard';
import { extname } from 'path';
import { Telemetry } from '../helpers/Telemetry';
import { writeFileAsync, copyFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Template {
/**
@@ -31,27 +33,30 @@ export class Template {
const clonedArticle = Object.assign({}, article);
const titleValue = await vscode.window.showInputBox({
title: `Template title`,
prompt: `What name would you like to give your template?`,
placeHolder: `article`,
title: l10n.t(LocalizationKey.commandsTemplateGenerateInputTitle),
prompt: l10n.t(LocalizationKey.commandsTemplateGenerateInputPrompt),
placeHolder: l10n.t(LocalizationKey.commandsTemplateGenerateInputPlaceholder),
ignoreFocusOut: true
});
if (!titleValue) {
Notifications.warning(`You did not specify a template title.`);
Notifications.warning(l10n.t(LocalizationKey.commandsTemplateGenerateNoTitleWarning));
return;
}
const keepContents = await vscode.window.showQuickPick(['yes', 'no'], {
title: `Keep contents`,
canPickMany: false,
placeHolder: `Do you want to keep the contents for the template?`,
ignoreFocusOut: true
});
const keepContents = await vscode.window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
title: l10n.t(LocalizationKey.commandsTemplateGenerateKeepContentsTitle),
placeHolder: l10n.t(LocalizationKey.commandsTemplateGenerateKeepContentsPlaceholder),
canPickMany: false,
ignoreFocusOut: true
}
);
if (!keepContents) {
Notifications.warning(
`You did not pick any of the options for keeping the template its content.`
l10n.t(LocalizationKey.commandsTemplateGenerateKeepContentsNoOptionWarning)
);
return;
}
@@ -60,14 +65,16 @@ export class Template {
const templatePath = Project.templatePath();
if (templatePath) {
const fileContents = ArticleHelper.stringifyFrontMatter(
keepContents === 'no' ? '' : clonedArticle.content,
keepContents === l10n.t(LocalizationKey.commonNo) ? '' : clonedArticle.content,
clonedArticle.data
);
const templateFile = path.join(templatePath.fsPath, `${titleValue}.${fileType}`);
await writeFileAsync(templateFile, fileContents, { encoding: 'utf-8' });
Notifications.info(`Template created and is now available in your ${folder} folder.`);
Notifications.info(
l10n.t(LocalizationKey.commandsTemplateGenerateKeepContentsSuccess, folder)
);
}
}
}
@@ -79,7 +86,7 @@ export class Template {
const folder = Settings.get<string>(SETTING_TEMPLATES_FOLDER);
if (!folder) {
Notifications.warning(`No templates found.`);
Notifications.warning(l10n.t(LocalizationKey.commandsTemplateGetTemplatesWarning));
return;
}
@@ -96,26 +103,28 @@ export class Template {
const contentTypes = ContentType.getAll();
if (!folderPath) {
Notifications.warning(`Incorrect project folder path retrieved.`);
Notifications.warning(l10n.t(LocalizationKey.commandsTemplateCreateFolderPathWarning));
return;
}
const templates = await Template.getTemplates();
if (!templates || templates.length === 0) {
Notifications.warning(`No templates found.`);
Notifications.warning(l10n.t(LocalizationKey.commandsTemplateCreateNoTemplatesWarning));
return;
}
const selectedTemplate = await vscode.window.showQuickPick(
templates.map((t) => path.basename(t.fsPath)),
{
title: `Select a template`,
placeHolder: `Select the content template to use`,
title: l10n.t(LocalizationKey.commandsTemplateCreateSelectTemplateTitle),
placeHolder: l10n.t(LocalizationKey.commandsTemplateCreateSelectTemplatePlaceholder),
ignoreFocusOut: true
}
);
if (!selectedTemplate) {
Notifications.warning(`No template selected.`);
Notifications.warning(
l10n.t(LocalizationKey.commandsTemplateCreateSelectTemplateNoTemplateWarning)
);
return;
}
@@ -127,7 +136,9 @@ export class Template {
// Start the template read
const template = templates.find((t) => t.fsPath.endsWith(selectedTemplate));
if (!template) {
Notifications.warning(`Content template could not be found.`);
Notifications.warning(
l10n.t(LocalizationKey.commandsTemplateCreateSelectTemplateNotFoundWarning)
);
return;
}
@@ -154,7 +165,7 @@ export class Template {
// Update the properties inside the template
let frontMatter = await ArticleHelper.getFrontMatterByPath(newFilePath);
if (!frontMatter) {
Notifications.warning(`Something failed when retrieving the newly created file.`);
Notifications.warning(l10n.t(LocalizationKey.commonError));
return;
}
@@ -185,7 +196,7 @@ export class Template {
vscode.window.showTextDocument(txtDoc);
}
Notifications.info(`Your new content has been created.`);
Notifications.info(l10n.t(LocalizationKey.commandsTemplateCreateSuccess));
Telemetry.send(TelemetryEvent.createContentFromTemplate);

View File

@@ -1,6 +1,8 @@
import { commands, window, Selection, QuickPickItem, TextEditor } from 'vscode';
import { COMMAND_NAME, CONTEXT, SETTING_CONTENT_WYSIWYG } from '../constants';
import { Settings } from '../helpers';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
enum MarkupType {
bold = 1,
@@ -81,45 +83,45 @@ export class Wysiwyg {
commands.registerCommand(COMMAND_NAME.options, async () => {
const qpItems: QuickPickItem[] = [
{
label: '$(list-unordered) Unordered list',
detail: 'Add an unordered list',
label: `$(list-unordered) ${LocalizationKey.commandsWysiwygCommandUnorderedListLabel}`,
detail: LocalizationKey.commandsWysiwygCommandUnorderedListDetail,
alwaysShow: true
},
{
label: '$(list-ordered) Ordered list',
detail: 'Add an ordered list',
label: `$(list-ordered) ${LocalizationKey.commandsWysiwygCommandOrderedListLabel}`,
detail: LocalizationKey.commandsWysiwygCommandOrderedListDetail,
alwaysShow: true
},
{
label: '$(tasklist) Task list',
detail: 'Add a task list',
label: `$(tasklist) ${LocalizationKey.commandsWysiwygCommandTaskListLabel}`,
detail: LocalizationKey.commandsWysiwygCommandTaskListDetail,
alwaysShow: true
},
{
label: '$(code) Code',
detail: 'Add inline code snippet',
label: `$(code) ${LocalizationKey.commandsWysiwygCommandCodeLabel}`,
detail: LocalizationKey.commandsWysiwygCommandCodeDetail,
alwaysShow: true
},
{
label: '$(symbol-namespace) Code block',
detail: 'Add a code block',
label: `$(symbol-namespace) ${LocalizationKey.commandsWysiwygCommandCodeblockLabel}`,
detail: LocalizationKey.commandsWysiwygCommandCodeblockDetail,
alwaysShow: true
},
{
label: '$(quote) Blockquote',
detail: 'Add a blockquote',
label: `$(quote) ${LocalizationKey.commandsWysiwygCommandBlockquoteLabel}`,
detail: LocalizationKey.commandsWysiwygCommandBlockquoteDetail,
alwaysShow: true
},
{
label: '$(symbol-text) Strikethrough',
detail: 'Add a strikethrough',
label: `$(symbol-text) ${LocalizationKey.commandsWysiwygCommandStrikethroughLabel}`,
detail: LocalizationKey.commandsWysiwygCommandStrikethroughDetail,
alwaysShow: true
}
];
const option = await window.showQuickPick([...qpItems], {
title: 'WYSIWYG Options',
placeHolder: 'Which type of markup would you like to insert?',
title: l10n.t(LocalizationKey.commandsWysiwygQuickPickTitle),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygQuickPickPlaceholder),
canPickMany: false,
ignoreFocusOut: true
});
@@ -209,17 +211,17 @@ export class Wysiwyg {
const linkText = hasTextSelection ? editor.document.getText(selection) : '';
const link = await window.showInputBox({
title: 'WYSIWYG Hyperlink',
placeHolder: 'Enter the URL',
prompt: 'Enter the URL',
title: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputTitle),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputPrompt),
prompt: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkHyperlinkInputPrompt),
value: linkText,
ignoreFocusOut: true
});
const text = await window.showInputBox({
title: 'WYSIWYG Text',
prompt: 'Enter the text for the hyperlink',
placeHolder: 'Enter the text for the hyperlink',
title: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkTextInputTitle),
prompt: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkTextInputPrompt),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygAddHyperlinkTextInputPrompt),
value: linkText,
ignoreFocusOut: true
});
@@ -278,9 +280,9 @@ export class Wysiwyg {
const headingLvl = await window.showQuickPick(
['Heading 1', 'Heading 2', 'Heading 3', 'Heading 4', 'Heading 5', 'Heading 6'],
{
title: 'Heading Level',
title: l10n.t(LocalizationKey.commandsWysiwygInsertTextHeadingInputTitle),
placeHolder: l10n.t(LocalizationKey.commandsWysiwygInsertTextHeadingInputPlaceholder),
canPickMany: false,
placeHolder: 'Which heading level do you want to insert?',
ignoreFocusOut: true
}
);

View File

@@ -3,7 +3,7 @@ import { ContentType } from './../models/PanelSettings';
export const DEFAULT_CONTENT_TYPE_NAME = 'default';
export const DEFAULT_CONTENT_TYPE: ContentType = {
name: 'default',
name: DEFAULT_CONTENT_TYPE_NAME,
pageBundle: false,
previewPath: null,
fields: [

View File

@@ -1,4 +1,4 @@
export const EXTENSION_NAME = '𝖥𝗋𝗈𝗇𝗍 𝖬𝖺𝗍𝗍𝖾𝗋 𝖢𝖬𝖲';
export const EXTENSION_NAME = 'Front Matter CMS';
export const CONFIG_KEY = 'frontMatter';
@@ -29,6 +29,7 @@ export const SETTING_SLUG_UPDATE_FILE_NAME = 'taxonomy.alignFilename';
export const SETTING_INDENT_ARRAY = 'taxonomy.indentArrays';
export const SETTING_REMOVE_QUOTES = 'taxonomy.noPropertyValueQuotes';
export const SETTING_QUOTE_STRINGS = 'taxonomy.quoteStringValues';
export const SETTING_FRONTMATTER_TYPE = 'taxonomy.frontMatterType';

View File

@@ -4,6 +4,7 @@ export enum DashboardMessage {
setPageViewType = 'setPageViewType',
getMode = 'getMode',
showWarning = 'showWarning',
openConfig = 'openConfig',
// Project switching
switchProject = 'switchProject',

View File

@@ -3,18 +3,20 @@ import * as l10n from '@vscode/l10n';
import { messageHandler } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../../DashboardMessage';
import { AstroCollection } from '../../../../models';
import { Settings } from '../../../models';
import { Settings, Status } from '../../../models';
import { SelectItem } from '../../Steps/SelectItem';
import { LocalizationKey } from '../../../../localization';
export interface IAstroContentTypesProps {
settings: Settings
triggerLoading: (isLoading: boolean) => void;
setStatus: (status: Status) => void;
}
export const AstroContentTypes: React.FunctionComponent<IAstroContentTypesProps> = ({
settings,
triggerLoading
triggerLoading,
setStatus
}: React.PropsWithChildren<IAstroContentTypesProps>) => {
const [collections, setCollections] = React.useState<AstroCollection[]>([]);
@@ -26,12 +28,26 @@ export const AstroContentTypes: React.FunctionComponent<IAstroContentTypesProps>
});
}, []);
React.useEffect(() => {
if (collections.length > 0 && settings?.contentTypes?.length > 0) {
// Find created content types from the collections
const astroCollection = collections.find(c => settings.contentTypes.find((ct) => ct.name === c.name));
console.log(`astroCollection`, astroCollection)
if (astroCollection) {
setStatus(Status.Completed);
} else {
setStatus(Status.Active);
}
}
}, [collections, settings.contentTypes])
const generateContentType = (collection: AstroCollection) => {
triggerLoading(true);
messageHandler.request(DashboardMessage.ssgSetAstroContentTypes, {
collection
}).then((result) => {
triggerLoading(false);
setStatus(Status.Completed);
});
}

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { NavigationType, Page } from '../../models';
import { DashboardViewAtom, SettingsSelector } from '../../state';
import { useRecoilValue } from 'recoil';
import { Page } from '../../models';
import { SettingsSelector } from '../../state';
import { Overview } from './Overview';
import { Spinner } from '../Common/Spinner';
import { SponsorMsg } from '../Layout/SponsorMsg';
@@ -23,13 +23,10 @@ export const Contents: React.FunctionComponent<IContentsProps> = ({
}: React.PropsWithChildren<IContentsProps>) => {
const settings = useRecoilValue(SettingsSelector);
const { pageItems } = usePages(pages);
const [, setView] = useRecoilState(DashboardViewAtom);
const pageFolders = [...new Set(pageItems.map((page) => page.fmFolder))];
useEffect(() => {
setView(NavigationType.Contents);
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewContentsView
});

View File

@@ -1,7 +1,7 @@
import * as React from 'react';
import { Header } from '../Header';
import { useRecoilState, useRecoilValue } from 'recoil';
import { DashboardViewAtom, SettingsSelector } from '../../state';
import { useRecoilValue } from 'recoil';
import { SettingsSelector } from '../../state';
import { DataForm } from './DataForm';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { DataFile } from '../../../models/DataFile';
@@ -36,7 +36,6 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
const [dataEntries, setDataEntries] = useState<any | any[] | null>(null);
const settings = useRecoilValue(SettingsSelector);
const { getColors } = useThemeColors();
const [, setView] = useRecoilState(DashboardViewAtom);
const setSchema = (dataFile: DataFile) => {
setSelectedData(dataFile);
@@ -137,7 +136,6 @@ export const DataView: React.FunctionComponent<IDataViewProps> = (
}, [selectedData, , dataEntries, selectedIndex]);
useEffect(() => {
setView(NavigationType.Data);
Messenger.listen(messageListener);
Messenger.send(DashboardMessage.sendTelemetry, {

View File

@@ -1,9 +1,8 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { UploadIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useRecoilValue } from 'recoil';
import {
DashboardViewAtom,
LoadingAtom,
MediaFoldersAtom,
SelectedMediaFolderAtom,
@@ -43,7 +42,6 @@ export const Media: React.FunctionComponent<IMediaProps> = (
const folders = useRecoilValue(MediaFoldersAtom);
const loading = useRecoilValue(LoadingAtom);
const { getColors } = useThemeColors();
const [, setView] = useRecoilState(DashboardViewAtom);
const currentStaticFolder = useMemo(() => {
if (settings?.staticFolder) {
@@ -153,7 +151,6 @@ export const Media: React.FunctionComponent<IMediaProps> = (
);
useEffect(() => {
setView(NavigationType.Media);
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewMediaView
});

View File

@@ -76,7 +76,8 @@ export const SettingsView: React.FunctionComponent<ISettingsViewProps> = (_: Rea
<AstroContentTypes
settings={settings}
triggerLoading={(isLoading) => setLoading(isLoading)} />
triggerLoading={(isLoading) => setLoading(isLoading)}
setStatus={_ => null} />
</div>
</VSCodePanelView>
)

View File

@@ -2,14 +2,14 @@ import { Messenger } from '@estruyf/vscode/dist/client';
import { CodeIcon, PlusSmIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useRecoilValue } from 'recoil';
import { FeatureFlag } from '../../../components/features/FeatureFlag';
import { FEATURE_FLAG } from '../../../constants';
import { TelemetryEvent } from '../../../constants/TelemetryEvent';
import { SnippetParser } from '../../../helpers/SnippetParser';
import { DashboardMessage } from '../../DashboardMessage';
import useThemeColors from '../../hooks/useThemeColors';
import { DashboardViewAtom, ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
import { ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
import { FilterInput } from '../Header/FilterInput';
import { PageLayout } from '../Layout/PageLayout';
import { FormDialog } from '../Modals/FormDialog';
@@ -18,7 +18,6 @@ import { Item } from './Item';
import { NewForm } from './NewForm';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { NavigationType } from '../../models';
export interface ISnippetsProps { }
@@ -35,7 +34,6 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
const [mediaSnippet, setMediaSnippet] = useState(false);
const [snippetFilter, setSnippetFilter] = useState<string>('');
const { getColors } = useThemeColors();
const [, setView] = useRecoilState(DashboardViewAtom);
const snippets = settings?.snippets || {};
const snippetKeys = useMemo(() => {
@@ -84,7 +82,6 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
};
useEffect(() => {
setView(NavigationType.Snippets);
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewSnippetsView
});

View File

@@ -4,7 +4,6 @@ import { CheckCircleIcon as CheckCircleIconSolid, PlusCircleIcon as PlusCircleIc
export interface ISelectItemProps {
title: string;
icon?: "add" | "select";
buttonTitle: string;
isSelected: boolean;
disabled?: boolean;
@@ -13,7 +12,6 @@ export interface ISelectItemProps {
export const SelectItem: React.FunctionComponent<ISelectItemProps> = ({
title,
icon = "select",
buttonTitle,
isSelected,
disabled,
@@ -30,17 +28,9 @@ export const SelectItem: React.FunctionComponent<ISelectItemProps> = ({
disabled={disabled}
>
{isSelected ? (
icon === "add" ? (
<PlusCircleIconSolid className={`h-4 w-4`} />
) : (
<CheckCircleIconSolid className={`h-4 w-4`} />
)
<CheckCircleIconSolid className={`h-4 w-4`} />
) : (
icon === "add" ? (
<PlusCircleIcon className={`h-4 w-4`} />
) : (
<CheckCircleIcon className={`h-4 w-4`} />
)
<PlusCircleIcon className={`h-4 w-4`} />
)}
<span>{title}</span>
</button>

View File

@@ -31,6 +31,7 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
const [framework, setFramework] = useState<string | null>(null);
const [taxImported, setTaxImported] = useState<boolean>(false);
const [templates, setTemplates] = useState<Template[]>([]);
const [astroCollectionsStatus, setAstroCollectionsStatus] = useState<Status>(Status.Optional)
const { getColors } = useThemeColors();
const frameworks: Framework[] = FrameworkDetectors.map((detector: any) => detector.framework);
@@ -54,6 +55,10 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
});
}
const showNotification = () => {
Messenger.send(DashboardMessage.openConfig);
};
const reload = () => {
const crntState: any = Messenger.getState() || {};
@@ -175,6 +180,9 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
))
}
</div>
<p className='mt-4 text-[var(--vscode-editorWarning-foreground)]'>
<b>{l10n.t(LocalizationKey.commonImportant)}</b>: {l10n.t(LocalizationKey.dashboardStepsStepsToGetStartedTemplateWarning)}</p>
</div>
),
show: (crntTemplates || []).length > 0,
@@ -186,10 +194,11 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
description: (
<AstroContentTypes
settings={settings}
triggerLoading={(isLoading) => setLoading(isLoading)} />
triggerLoading={(isLoading) => setLoading(isLoading)}
setStatus={(status) => setAstroCollectionsStatus(status)} />
),
show: settings.crntFramework === 'astro',
status: Status.Optional
status: astroCollectionsStatus
},
{
id: `welcome-content-folders`,
@@ -255,11 +264,14 @@ export const StepsToGetStarted: React.FunctionComponent<IStepsToGetStartedProps>
: Status.NotStarted,
onClick:
settings.initialized && settings.contentFolders && settings.contentFolders.length > 0
? reload
? () => {
showNotification();
reload();
}
: undefined
}
]
), [settings, framework, taxImported, templates]);
), [settings, framework, taxImported, templates, astroCollectionsStatus]);
React.useEffect(() => {
if (settings.crntFramework || settings.framework?.name) {

View File

@@ -2,12 +2,12 @@ import { Messenger } from '@estruyf/vscode/dist/client';
import { ChevronRightIcon, DownloadIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useRecoilValue } from 'recoil';
import { TelemetryEvent } from '../../../constants';
import { TaxonomyData } from '../../../models';
import { DashboardMessage } from '../../DashboardMessage';
import { NavigationType, Page } from '../../models';
import { DashboardViewAtom, SettingsSelector } from '../../state';
import { Page } from '../../models';
import { SettingsSelector } from '../../state';
import { NavigationBar, NavigationItem } from '../Layout';
import { PageLayout } from '../Layout/PageLayout';
import { SponsorMsg } from '../Layout/SponsorMsg';
@@ -25,7 +25,6 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
const settings = useRecoilValue(SettingsSelector);
const [taxonomySettings, setTaxonomySettings] = useState<TaxonomyData>();
const [selectedTaxonomy, setSelectedTaxonomy] = useState<string | null>(`tags`);
const [, setView] = useRecoilState(DashboardViewAtom);
const onImport = () => {
Messenger.send(DashboardMessage.importTaxonomy);
@@ -40,7 +39,6 @@ export const TaxonomyView: React.FunctionComponent<ITaxonomyViewProps> = ({
}, [settings?.tags, settings?.categories, settings?.customTaxonomy]);
useEffect(() => {
setView(NavigationType.Taxonomy);
Messenger.send(DashboardMessage.sendTelemetry, {
event: TelemetryEvent.webviewTaxonomyDashboard
});

View File

@@ -1,6 +1,6 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { EventData } from '@estruyf/vscode/dist/models';
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useMemo } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { MediaInfo, MediaPaths } from '../../models';
import { DashboardCommand } from '../DashboardCommand';
@@ -52,27 +52,11 @@ export default function useMedia() {
}
}, [search, prevSearch]);
const getMedia = useCallback(() => {
const allMedia = useMemo(() => {
return searchedMedia.slice(page * pageSetNr, (page + 1) * pageSetNr);
}, [searchedMedia, page, pageSetNr]);
const messageListener = (
message: MessageEvent<EventData<MediaPaths | { key: string; value: any }>>
) => {
if (message.data.command === DashboardCommand.media) {
const payload: MediaPaths = message.data.payload as MediaPaths;
setLoading(false);
setMedia(payload.media);
setTotal(payload.total);
setFolders(payload.folders);
setSelectedFolder(payload.selectedFolder);
setSearchedMedia(payload.media);
setAllContentFolders(payload.allContentFolders);
setAllStaticFolders(payload.allStaticfolders);
}
};
useEffect(() => {
const searchMedia = (search: string, media: MediaInfo[]) => {
if (search) {
const fuse = new Fuse(media, fuseOptions);
const results = fuse.search(search);
@@ -86,6 +70,28 @@ export default function useMedia() {
setTotal(media.length);
setSearchedMedia(media);
}
const messageListener = useCallback((message: MessageEvent<EventData<MediaPaths | { key: string; value: any }>>) => {
if (message.data.command === DashboardCommand.media) {
const payload: MediaPaths = message.data.payload as MediaPaths;
setLoading(false);
setMedia(payload.media);
setTotal(payload.total);
setFolders(payload.folders);
setSelectedFolder(payload.selectedFolder);
if (search) {
searchMedia(search, payload.media);
} else {
setSearchedMedia(payload.media);
}
setAllContentFolders(payload.allContentFolders);
setAllStaticFolders(payload.allStaticfolders);
}
}, [search]);
useEffect(() => {
searchMedia(search, media);
}, [search, media]);
useEffect(() => {
@@ -94,9 +100,9 @@ export default function useMedia() {
return () => {
Messenger.unlisten(messageListener);
};
}, []);
}, [search]);
return {
media: getMedia()
media: allMedia
};
}

View File

@@ -144,9 +144,18 @@
@apply bg-gray-500 opacity-50;
}
}
}
.fields {
> .fields {
> div > label:first-of-type {
color: var(--primary-color);
}
> div:not(:first-child) > label:first-of-type {
margin-block-start: 2rem;
border-block-start: 1px solid;
padding-block-start: 2rem;
}
}
}
.ant-list.ant-list-bordered {

View File

@@ -1,6 +1,6 @@
import { GitListener } from './listeners/general/GitListener';
import * as vscode from 'vscode';
import { COMMAND_NAME, TelemetryEvent } from './constants';
import { COMMAND_NAME, EXTENSION_NAME, TelemetryEvent } from './constants';
import { MarkdownFoldingProvider } from './providers/MarkdownFoldingProvider';
import { TagType } from './panelWebView/TagType';
import { PanelProvider } from './panelWebView/PanelProvider';
@@ -281,7 +281,7 @@ export async function activate(context: vscode.ExtensionContext) {
);
fmStatusBarItem.command = COMMAND_NAME.dashboard;
fmStatusBarItem.text = `$(fm-logo)`;
fmStatusBarItem.tooltip = `Front Matter CMS`;
fmStatusBarItem.tooltip = EXTENSION_NAME;
fmStatusBarItem.show();
// Register listeners that make sure the status bar updates

View File

@@ -42,6 +42,8 @@ import { CustomScript } from './CustomScript';
import { Folders } from '../commands/Folders';
import { existsAsync, readFileAsync } from '../utils';
import { mkdirAsync } from '../utils/mkdirAsync';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class ArticleHelper {
private static notifiedFiles: string[] = [];
@@ -442,7 +444,11 @@ export class ArticleHelper {
const newFolder = join(folderPath, sanitizedName);
if (await existsAsync(newFolder)) {
Notifications.error(
`A page bundle with the name ${sanitizedName} already exists in ${folderPath}`
l10n.t(
LocalizationKey.helpersArticleHelperCreateContentPageBundleError,
sanitizedName,
folderPath
)
);
return;
} else {
@@ -466,7 +472,9 @@ export class ArticleHelper {
await mkdirAsync(folderPath, { recursive: true });
if (await existsAsync(newFilePath)) {
Notifications.warning(`Content with the title already exists. Please specify a new title.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersArticleHelperCreateContentContentExistsWarning)
);
return;
}
}
@@ -597,7 +605,12 @@ export class ArticleHelper {
value = value.replace(regex, updatedValue);
}
} catch (e) {
Notifications.error(`Error while processing the ${placeholder.id} placeholder`);
Notifications.error(
l10n.t(
LocalizationKey.helpersArticleHelperProcessCustomPlaceholdersPlaceholderError,
placeholder.id
)
);
Logger.error((e as Error).message);
value = DefaultFieldValues.faultyCustomPlaceholder;
@@ -795,9 +808,10 @@ export class ArticleHelper {
Extension.getInstance().diagnosticCollection.set(editor.document.uri, [
{
severity: DiagnosticSeverity.Error,
message: `${
error.name ? `${error.name}: ` : ''
}Error parsing the front matter of ${fileName}`,
message: `${error.name ? `${error.name}: ` : ''}${l10n.t(
LocalizationKey.helpersArticleHelperParseFileDiagnosticError,
fileName
)}`,
range: fmRange
}
]);

View File

@@ -1,8 +1,9 @@
import { ModeListener } from './../listeners/general/ModeListener';
import { PagesListener } from './../listeners/dashboard';
import { ArticleHelper, CustomScript, Settings } from '.';
import { ArticleHelper, CustomScript, Logger, Settings } from '.';
import {
DefaultFieldValues,
EXTENSION_NAME,
FEATURE_FLAG,
SETTING_CONTENT_DRAFT_FIELD,
SETTING_DATE_FORMAT,
@@ -28,7 +29,9 @@ import { Telemetry } from './Telemetry';
import { processKnownPlaceholders } from './PlaceholderHelper';
import { basename } from 'path';
import { ParsedFrontMatter } from '../parsers';
import { encodeEmoji, existsAsync, writeFileAsync } from '../utils';
import { encodeEmoji, existsAsync, fieldWhenClause, writeFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class ContentType {
/**
@@ -173,30 +176,34 @@ export class ContentType {
const filePath = editor?.document.uri.fsPath;
if (!content || !content.data) {
Notifications.warning(`No front matter data found to generate a content type.`);
Notifications.warning(l10n.t(LocalizationKey.helpersContentTypeGenerateNoFrontMatterError));
return;
}
const override = await window.showQuickPick(['Yes', 'No'], {
title: 'Override default content type',
placeHolder:
'Do you want to overwrite the default content type configuration with the fields used in the current field?',
ignoreFocusOut: true
});
const overrideBool = override === 'Yes';
const override = await window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
title: l10n.t(LocalizationKey.helpersContentTypeGenerateOverrideQuickPickTitle),
placeHolder: l10n.t(LocalizationKey.helpersContentTypeGenerateOverrideQuickPickPlaceholder),
ignoreFocusOut: true
}
);
const overrideBool = override === l10n.t(LocalizationKey.commonYes);
let contentTypeName: string | undefined = `default`;
// Ask for the new content type name
if (!overrideBool) {
contentTypeName = await window.showInputBox({
title: 'Generate Content Type',
placeHolder: 'Enter the name of the content type to generate',
prompt: 'Enter the name of the content type to generate',
title: l10n.t(LocalizationKey.helpersContentTypeGenerateContentTypeInputTitle),
placeHolder: l10n.t(LocalizationKey.helpersContentTypeGenerateContentTypeInputPrompt),
prompt: l10n.t(LocalizationKey.helpersContentTypeGenerateContentTypeInputPrompt),
ignoreFocusOut: true,
validateInput: (value: string) => {
if (!value) {
return 'Please enter a name for the content type';
return l10n.t(
LocalizationKey.helpersContentTypeGenerateContentTypeInputValidationEnterName
);
}
const contentTypes = ContentType.getAll();
@@ -204,7 +211,9 @@ export class ContentType {
contentTypes &&
contentTypes.find((ct) => ct.name.toLowerCase() === value.toLowerCase())
) {
return 'A content type with this name already exists';
return l10n.t(
LocalizationKey.helpersContentTypeGenerateContentTypeInputValidationNameExists
);
}
return null;
@@ -212,7 +221,9 @@ export class ContentType {
});
if (!contentTypeName) {
Notifications.warning(`You didn't specify a name for the content type.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersContentTypeGenerateNoContentTypeNameWarning)
);
return;
}
}
@@ -221,12 +232,17 @@ export class ContentType {
let pageBundle = false;
const fileName = filePath ? basename(filePath) : undefined;
if (fileName?.startsWith(`index.`)) {
const pageBundleAnswer = await window.showQuickPick(['Yes', 'No'], {
title: 'Use as page bundle',
placeHolder: 'Do you want to use this content type as a page bundle?',
ignoreFocusOut: true
});
pageBundle = pageBundleAnswer === 'Yes';
const pageBundleAnswer = await window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
title: l10n.t(LocalizationKey.helpersContentTypeGeneratePageBundleQuickPickTitle),
placeHolder: l10n.t(
LocalizationKey.helpersContentTypeGeneratePageBundleQuickPickPlaceHolder
),
ignoreFocusOut: true
}
);
pageBundle = pageBundleAnswer === l10n.t(LocalizationKey.commonYes);
}
const fields = ContentType.generateFields(content.data);
@@ -256,11 +272,19 @@ export class ContentType {
const configPath = await Settings.projectConfigPath();
const notificationAction = await Notifications.info(
`Content type ${contentTypeName} has been ${overrideBool ? `updated` : `generated`}.`,
configPath && (await existsAsync(configPath)) ? `Open settings` : undefined
overrideBool
? l10n.t(LocalizationKey.helpersContentTypeGenerateUpdatedSuccess)
: l10n.t(LocalizationKey.helpersContentTypeGenerateGeneratedSuccess),
configPath && (await existsAsync(configPath))
? l10n.t(LocalizationKey.commonOpenSettings)
: undefined
);
if (notificationAction === 'Open settings' && configPath && (await existsAsync(configPath))) {
if (
notificationAction === l10n.t(LocalizationKey.commonOpenSettings) &&
configPath &&
(await existsAsync(configPath))
) {
commands.executeCommand('vscode.open', Uri.file(configPath));
}
}
@@ -278,7 +302,9 @@ export class ContentType {
const content = ArticleHelper.getCurrent();
if (!content || !content.data) {
Notifications.warning(`No front matter data found to add missing fields.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersContentTypeAddMissingFieldsNoFrontMatterWarning)
);
return;
}
@@ -293,11 +319,17 @@ export class ContentType {
const configPath = await Settings.projectConfigPath();
const notificationAction = await Notifications.info(
`Content type ${contentType.name} has been updated.`,
configPath && (await existsAsync(configPath)) ? `Open settings` : undefined
l10n.t(LocalizationKey.helpersContentTypeAddMissingFieldsUpdatedSuccess, contentType.name),
configPath && (await existsAsync(configPath))
? l10n.t(LocalizationKey.commonOpenSettings)
: undefined
);
if (notificationAction === 'Open settings' && configPath && (await existsAsync(configPath))) {
if (
notificationAction === l10n.t(LocalizationKey.commonOpenSettings) &&
configPath &&
(await existsAsync(configPath))
) {
commands.executeCommand('vscode.open', Uri.file(configPath));
}
}
@@ -316,16 +348,18 @@ export class ContentType {
const contentTypes = ContentType.getAll() || [];
if (!content || !content.data) {
Notifications.warning(`No front matter data found to set the content type.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersContentTypeSetContentTypeNoFrontMatterWarning)
);
return;
}
const ctAnswer = await window.showQuickPick(
contentTypes.map((ct) => ct.name),
{
title: 'Select the content type',
ignoreFocusOut: true,
placeHolder: 'Which content type would you like to use?'
title: l10n.t(LocalizationKey.helpersContentTypeSetContentTypeQuickPickTitle),
placeHolder: l10n.t(LocalizationKey.helpersContentTypeSetContentTypeQuickPickPlaceholder),
ignoreFocusOut: true
}
);
@@ -739,6 +773,20 @@ export class ContentType {
name: field,
type: 'number'
} as Field);
} else if (typeof fieldData === 'boolean') {
if (field.toLowerCase() === 'draft') {
fields.push({
title: field,
name: field,
type: 'draft'
} as Field);
} else {
fields.push({
title: field,
name: field,
type: 'boolean'
} as Field);
}
} else if (!isNaN(new Date(fieldData).getDate())) {
fields.push({
title: field,
@@ -776,10 +824,66 @@ export class ContentType {
window.withProgress(
{
location: ProgressLocation.Notification,
title: 'Front Matter: Creating content...',
title: l10n.t(LocalizationKey.helpersContentTypeCreateProgressTitle, EXTENSION_NAME),
cancellable: false
},
async () => {
if (contentType.isSubContent || contentType.allowAsSubContent) {
let showDialog = true;
if (contentType.allowAsSubContent) {
const subContentAnswer = await window.showQuickPick(
[l10n.t(LocalizationKey.commonNo), l10n.t(LocalizationKey.commonYes)],
{
title: l10n.t(LocalizationKey.helpersContentTypeCreateAllowSubContentTitle),
placeHolder: l10n.t(
LocalizationKey.helpersContentTypeCreateAllowSubContentPlaceHolder
),
ignoreFocusOut: true
}
);
showDialog = subContentAnswer === l10n.t(LocalizationKey.commonYes);
}
if (showDialog) {
const folderLocation = await window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
defaultUri: Uri.file(folderPath),
openLabel: l10n.t(
LocalizationKey.helpersContentTypeCreateAllowSubContentShowOpenDialogOpenLabel
),
title: l10n.t(
LocalizationKey.helpersContentTypeCreateAllowSubContentShowOpenDialogTitle
)
});
if (!folderLocation || folderLocation.length === 0) {
return;
}
folderPath = folderLocation[0].fsPath;
if (contentType.pageBundle) {
const createAsPageBundle = await window.showQuickPick(
[l10n.t(LocalizationKey.commonNo), l10n.t(LocalizationKey.commonYes)],
{
title: l10n.t(LocalizationKey.helpersContentTypeCreatePageBundleTitle),
placeHolder: l10n.t(
LocalizationKey.helpersContentTypeCreatePageBundlePlaceHolder
),
ignoreFocusOut: true
}
);
if (createAsPageBundle === l10n.t(LocalizationKey.commonNo)) {
contentType.pageBundle = false;
}
}
}
}
let titleValue = await Questions.ContentTitle();
if (!titleValue) {
return;
@@ -826,7 +930,13 @@ export class ContentType {
!!contentType.clearEmpty
);
data = ArticleHelper.updateDates(Object.assign({}, data));
const article: ParsedFrontMatter = {
content: '',
data: Object.assign({}, data),
path: newFilePath
};
data = ArticleHelper.updateDates(article);
if (contentType.name !== DEFAULT_CONTENT_TYPE_NAME) {
data['type'] = contentType.name;
@@ -851,7 +961,7 @@ export class ContentType {
await commands.executeCommand('vscode.open', Uri.file(newFilePath));
Notifications.info(`Your new content has been created.`);
Notifications.info(l10n.t(LocalizationKey.helpersContentTypeCreateSuccess));
Telemetry.send(TelemetryEvent.createContentFromContentType);
@@ -878,6 +988,11 @@ export class ContentType {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
for (const field of obj.fields) {
if (!fieldWhenClause(field, data, obj.fields)) {
Logger.info(`Field ${field.name} not added because of when clause`);
continue;
}
if (field.name === 'title') {
if (field.default) {
data[field.name] = processKnownPlaceholders(
@@ -992,7 +1107,7 @@ export class ContentType {
private static async verify() {
const hasFeature = await ModeListener.hasFeature(FEATURE_FLAG.panel.contentType);
if (!hasFeature) {
Notifications.warning(`The content type actions are not available in this mode.`);
Notifications.warning(l10n.t(LocalizationKey.helpersContentTypeVerifyWarning));
return false;
}

View File

@@ -15,6 +15,8 @@ import { ParsedFrontMatter } from '../parsers';
import { TelemetryEvent } from '../constants/TelemetryEvent';
import { SETTING_CUSTOM_SCRIPTS } from '../constants';
import { existsAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class CustomScript {
/**
@@ -101,7 +103,9 @@ export class CustomScript {
}
);
} else {
Notifications.warning(`${script.title}: Article couldn't be retrieved.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersCustomScriptSingleRunArticleWarning, script.title)
);
}
}
@@ -115,7 +119,9 @@ export class CustomScript {
const folders = await Folders.getInfo();
if (!folders || folders.length === 0) {
Notifications.warning(`${script.title}: No files found.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersCustomScriptBulkRunNoFilesWarning, script.title)
);
return;
}
@@ -124,7 +130,7 @@ export class CustomScript {
window.withProgress(
{
location: ProgressLocation.Notification,
title: `Executing: ${script.title}`,
title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title),
cancellable: false
},
async (progress, token) => {
@@ -169,7 +175,9 @@ export class CustomScript {
script: ICustomScript
): Promise<void> {
if (!path) {
Notifications.error(`${script.title}: There was no folder or media path specified.`);
Notifications.error(
l10n.t(LocalizationKey.helpersCustomScriptRunMediaScriptNoFolderWarning, script.title)
);
return;
}
@@ -177,7 +185,7 @@ export class CustomScript {
window.withProgress(
{
location: ProgressLocation.Notification,
title: `Executing: ${script.title}`,
title: l10n.t(LocalizationKey.helpersCustomScriptExecuting, script.title),
cancellable: false
},
async () => {
@@ -196,7 +204,7 @@ export class CustomScript {
return;
} catch (e) {
Notifications.error(`${script.title}: ${(e as Error).message}`);
Notifications.errorWithOutput(`${script.title} -`);
return;
}
}
@@ -286,11 +294,15 @@ export class CustomScript {
} else if (editor) {
await ArticleHelper.update(editor, article);
} else {
Logger.error(`Couldn't update article.`);
throw new Error(`Couldn't update article.`);
}
Notifications.info(`${script.title}: front matter updated.`);
Notifications.info(
l10n.t(LocalizationKey.helpersCustomScriptShowOutputFrontMatterSuccess, script.title)
);
}
} else {
Logger.error(`No frontmatter found.`);
throw new Error(`No frontmatter found.`);
}
} catch (error) {
@@ -298,16 +310,21 @@ export class CustomScript {
ContentProvider.show(output, script.title, script.outputType || 'text');
} else {
window
.showInformationMessage(`${script.title}: ${output}`, 'Copy output')
.showInformationMessage(
`${script.title}: ${output}`,
l10n.t(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)
)
.then((value) => {
if (value === 'Copy output') {
if (value === l10n.t(LocalizationKey.helpersCustomScriptShowOutputCopyOutputAction)) {
vscodeEnv.clipboard.writeText(output);
}
});
}
}
} else {
Notifications.info(`${script.title}: Executed your custom script.`);
Notifications.info(
l10n.t(LocalizationKey.helpersCustomScriptShowOutputSuccess, script.title)
);
}
}
@@ -360,6 +377,7 @@ export class CustomScript {
}
if (!(await existsAsync(scriptPath))) {
Logger.error(`Script not found: ${scriptPath}`);
throw new Error(`Script not found: ${scriptPath}`);
}
@@ -368,7 +386,7 @@ export class CustomScript {
}
const fullScript = `${command} "${scriptPath}" ${args}`;
Logger.info(`Executing: ${fullScript}`);
Logger.info(l10n.t(LocalizationKey.helpersCustomScriptExecuting, fullScript));
const output: string = await CustomScript.executeScriptAsync(fullScript, wsPath);
@@ -416,6 +434,7 @@ export class CustomScript {
return new Promise(async (resolve, reject) => {
exec(fullScript, { cwd: wsPath }, (error, stdout) => {
if (error) {
Logger.error(error.message);
reject(error.message);
return;
}
@@ -441,7 +460,7 @@ export class CustomScript {
return true;
} catch (e) {
Logger.error(`Invalid command: ${command}`);
Logger.error(l10n.t(LocalizationKey.helpersCustomScriptValidateCommandError, command));
return false;
}
}

View File

@@ -7,6 +7,8 @@ import { commands } from 'vscode';
import { COMMAND_NAME, SETTING_DATA_FILES } from '../constants';
import { Settings } from './SettingsHelper';
import { existsAsync, readFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class DataFileHelper {
/**
@@ -54,21 +56,13 @@ export class DataFileHelper {
const dataFile = await DataFileHelper.get(file);
if (fileType === 'yaml') {
return yaml.safeLoad(dataFile || '');
return yaml.load(dataFile || '');
} else {
return dataFile ? JSON.parse(dataFile) : undefined;
}
} catch (ex) {
Logger.error(`DataFileHelper::process: ${(ex as Error).message}`);
const btnClick = await Notifications.error(
`Something went wrong while processing the data file. Check your file and output log for more information.`,
'Open output'
);
if (btnClick && btnClick === 'Open output') {
commands.executeCommand(COMMAND_NAME.showOutputChannel);
}
Notifications.errorWithOutput(l10n.t(LocalizationKey.helpersDataFileHelperProcessError));
return;
}
}

View File

@@ -33,6 +33,8 @@ import { Notifications } from './Notifications';
import { Settings } from './SettingsHelper';
import { TaxonomyHelper } from './TaxonomyHelper';
import { Cache } from '../commands/Cache';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Extension {
private static instance: Extension;
@@ -72,8 +74,8 @@ export class Extension {
}
if (usedVersion !== installedVersion) {
const whatIsNewTitle = `Check the changelog`;
const githubTitle = `Give it a ⭐️`;
const whatIsNewTitle = l10n.t(LocalizationKey.helpersExtensionGetVersionChangelog);
const githubTitle = l10n.t(LocalizationKey.helpersExtensionGetVersionStarIt);
const whatIsNew = {
title: whatIsNewTitle,
@@ -94,7 +96,11 @@ export class Extension {
window
.showInformationMessage(
`${EXTENSION_NAME} has been updated to v${installedVersion} — check out what's new!`,
l10n.t(
LocalizationKey.helpersExtensionGetVersionUpdateNotification,
EXTENSION_NAME,
installedVersion
),
starGitHub,
whatIsNew
)
@@ -215,11 +221,18 @@ export class Extension {
modifiedField?.teamValue
) {
Notifications.warning(
`The "${CONFIG_KEY}.${SETTING_DATE_FIELD}" and "${CONFIG_KEY}.${SETTING_MODIFIED_FIELD}" settings have been deprecated. Please use the "isPublishDate" and "isModifiedDate" datetime field properties instead.`,
'Hide',
'See migration guide'
l10n.t(
LocalizationKey.helpersExtensionMigrateSettingsDeprecatedWarning,
`${CONFIG_KEY}.${SETTING_DATE_FIELD}`,
`${CONFIG_KEY}.${SETTING_MODIFIED_FIELD}`
),
l10n.t(LocalizationKey.helpersExtensionMigrateSettingsDeprecatedWarningHide),
l10n.t(LocalizationKey.helpersExtensionMigrateSettingsDeprecatedWarningSeeGuide)
).then(async (value) => {
if (value === 'See migration guide') {
if (
value ===
l10n.t(LocalizationKey.helpersExtensionMigrateSettingsDeprecatedWarningSeeGuide)
) {
const isProd = this.isProductionMode;
commands.executeCommand(
'vscode.open',
@@ -234,7 +247,9 @@ export class Extension {
true,
'workspace'
);
} else if (value === 'Hide') {
} else if (
value === l10n.t(LocalizationKey.helpersExtensionMigrateSettingsDeprecatedWarningHide)
) {
await Extension.getInstance().setState<boolean>(
ExtensionState.Updates.v7_0_0.dateFields,
true,
@@ -291,15 +306,23 @@ export class Extension {
const templates = await Template.getTemplates();
if (templates && templates.length > 0) {
const answer = await window.showQuickPick(['Yes', 'No'], {
title: 'Front Matter - Templates',
placeHolder: 'Do you want to keep on using the template functionality?',
ignoreFocusOut: true
});
const answer = await window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
title: l10n.t(
LocalizationKey.helpersExtensionMigrateSettingsTemplatesQuickPickTitle,
EXTENSION_NAME
),
placeHolder: l10n.t(
LocalizationKey.helpersExtensionMigrateSettingsTemplatesQuickPickPlaceholder
),
ignoreFocusOut: true
}
);
await Settings.update(
SETTING_TEMPLATES_ENABLED,
answer?.toLocaleLowerCase() === 'yes',
answer?.toLocaleLowerCase() === l10n.t(LocalizationKey.commonYes),
true
);
}
@@ -410,9 +433,7 @@ export class Extension {
const mainVersionInstalled = extensions.getExtension(EXTENSION_ID);
if (mainVersionInstalled) {
Notifications.error(
`Front Matter BETA cannot be used while the main version is installed. Please ensure that you have only over version installed.`
);
Notifications.error(l10n.t(LocalizationKey.helpersExtensionCheckIfExtensionCanRunWarning));
return false;
}
}

View File

@@ -30,6 +30,8 @@ import { MediaListener as DashboardMediaListener } from '../listeners/dashboard'
import { ArticleHelper } from './ArticleHelper';
import { lookup } from 'mime-types';
import { existsAsync, readdirAsync, unlinkAsync, writeFileAsync } from '../utils';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class MediaHelpers {
private static media: MediaInfo[] = [];
@@ -308,7 +310,7 @@ export class MediaHelpers {
}
if (!(await existsAsync(absFolderPath))) {
Notifications.error(`We couldn't find your selected folder.`);
Notifications.error(l10n.t(LocalizationKey.helpersMediaHelperSaveFileFolderError));
return;
}
@@ -317,12 +319,22 @@ export class MediaHelpers {
if (imgData) {
await writeFileAsync(staticPath, imgData.data);
Notifications.info(`File ${fileName} uploaded to: ${folder}`);
Notifications.info(
l10n.t(
LocalizationKey.helpersMediaHelperSaveFileFileUploadedSuccess,
fileName,
folder || ''
)
);
return true;
} else {
Notifications.error(`Something went wrong uploading ${fileName}`);
throw new Error(`Something went wrong uploading ${fileName}`);
Notifications.error(
l10n.t(LocalizationKey.helpersMediaHelperSaveFileFileUploadedFailed, fileName)
);
throw new Error(
l10n.t(LocalizationKey.helpersMediaHelperSaveFileFileUploadedFailed, fileName)
);
}
}
@@ -355,8 +367,12 @@ export class MediaHelpers {
MediaHelpers.media = [];
return true;
} catch (err: any) {
Notifications.error(`Something went wrong deleting ${basename(file)}`);
throw new Error(`Something went wrong deleting ${basename(file)}`);
Notifications.error(
l10n.t(LocalizationKey.helpersMediaHelperDeleteFileFileDeletionFailed, basename(file))
);
throw new Error(
l10n.t(LocalizationKey.helpersMediaHelperDeleteFileFileDeletionFailed, basename(file))
);
}
}

View File

@@ -9,6 +9,8 @@ import { LocalStore } from '../constants';
import { existsAsync, renameAsync } from '../utils';
import { existsSync, mkdirSync, renameSync } from 'fs';
import { lookup } from 'mime-types';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
interface MediaRecord {
description: string;
@@ -160,14 +162,14 @@ export class MediaLibrary {
const newPath = join(dirname(filePath), `${newFileInfo.name}${oldFileInfo.ext}`);
if (await existsAsync(newPath)) {
Notifications.warning(`The name "${filename}" already exists at the file location.`);
Notifications.warning(LocalizationKey.helpersMediaLibraryRemoveWarning, filename);
} else {
await renameAsync(filePath, newPath);
await this.rename(filePath, newPath);
MediaHelpers.resetMedia();
}
} catch (err) {
Notifications.error(`Something went wrong updating "${name}" to "${filename}".`);
Notifications.error(l10n.t(LocalizationKey.helpersMediaLibraryRemoveError, name, filename));
}
}
}

View File

@@ -1,8 +1,10 @@
import { SETTING_GLOBAL_NOTIFICATIONS_DISABLED } from './../constants/settings';
import { window } from 'vscode';
import { EXTENSION_NAME, SETTING_GLOBAL_NOTIFICATIONS } from '../constants';
import { COMMAND_NAME, EXTENSION_NAME, SETTING_GLOBAL_NOTIFICATIONS } from '../constants';
import { Logger } from './Logger';
import { Settings } from './SettingsHelper';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
type NotificationType = 'INFO' | 'WARNING' | 'ERROR' | 'ERROR_ONCE';
@@ -57,6 +59,30 @@ export class Notifications {
return Promise.resolve(undefined);
}
/**
* Show an error notification to the user with a link to the output channel
* @param message
* @param items
* @returns
*/
public static errorWithOutput(message: string, ...items: any): Thenable<string | undefined> {
Logger.info(`${EXTENSION_NAME}: ${message}`, 'ERROR');
if (this.shouldShow('ERROR')) {
return window.showErrorMessage(
`${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 only once
* @param message

View File

@@ -29,7 +29,6 @@ import {
SETTING_SLUG_PREFIX,
SETTING_SLUG_SUFFIX,
SETTING_SLUG_UPDATE_FILE_NAME,
SETTING_TAXONOMY_CONTENT_TYPES,
SETTING_TAXONOMY_CUSTOM,
SETTING_TAXONOMY_FIELD_GROUPS,
SETTING_GIT_ENABLED,

View File

@@ -6,6 +6,8 @@ import { Notifications } from './Notifications';
import { Settings } from './SettingsHelper';
import { Logger } from './Logger';
import { SponsorAi } from '../services/SponsorAI';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Questions {
/**
@@ -14,12 +16,15 @@ export class Questions {
* @returns
*/
public static async yesOrNo(placeholder: string) {
const answer = await window.showQuickPick(['yes', 'no'], {
placeHolder: placeholder,
canPickMany: false,
ignoreFocusOut: true
});
return answer === 'yes';
const answer = await window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
placeHolder: placeholder,
canPickMany: false,
ignoreFocusOut: true
}
);
return answer === l10n.t(LocalizationKey.commonYes);
}
/**
@@ -36,9 +41,9 @@ export class Questions {
if (githubAuth && githubAuth.account.label) {
title = await window.showInputBox({
title: 'Title or description',
prompt: `What would you like to write about?`,
placeHolder: `Content title or description`,
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPlaceholder),
ignoreFocusOut: true
});
@@ -49,14 +54,18 @@ export class Questions {
if (aiTitles && aiTitles.length > 0) {
const options: QuickPickItem[] = [
{
label: `✏️ your title/description`,
label: `✏️ ${l10n.t(
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
)}`,
kind: QuickPickItemKind.Separator
},
{
label: title
},
{
label: `🤖 AI generated title`,
label: `🤖 ${l10n.t(
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
)}`,
kind: QuickPickItemKind.Separator
},
...aiTitles.map((d: string) => ({
@@ -65,8 +74,10 @@ export class Questions {
];
const selectedTitle = await window.showQuickPick(options, {
title: 'Select a title',
placeHolder: `Select a title for your content`,
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
placeHolder: l10n.t(
LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder
),
ignoreFocusOut: true
});
@@ -79,13 +90,11 @@ export class Questions {
}
} catch (e) {
Logger.error((e as Error).message);
Notifications.error(
`Failed fetching the AI title. Please try to use your own title or try again later.`
);
Notifications.error(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputFailed));
title = undefined;
}
} else if (!title && showWarning) {
Notifications.warning(`You did not specify a title for your content.`);
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
return;
}
}
@@ -93,15 +102,15 @@ export class Questions {
if (!title) {
title = await window.showInputBox({
title: 'Title',
prompt: `What would you like to use as a title for the content to create?`,
placeHolder: `Content title`,
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleTitleInputTitle),
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleTitleInputPrompt),
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleTitleInputPlaceholder),
ignoreFocusOut: true
});
}
if (!title && showWarning) {
Notifications.warning(`You did not specify a title for your content.`);
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleTitleInputWarning));
return;
}
@@ -123,20 +132,26 @@ export class Questions {
selectedFolder = await window.showQuickPick(
folders.map((f) => f.title),
{
title: `Select a folder`,
placeHolder: `Select where you want to create your content`,
title: l10n.t(LocalizationKey.helpersQuestionsSelectContentFolderQuickPickTitle),
placeHolder: l10n.t(
LocalizationKey.helpersQuestionsSelectContentFolderQuickPickPlaceholder
),
ignoreFocusOut: true
}
);
} else if (folders.length === 1) {
selectedFolder = folders[0].title;
} else {
Notifications.warning(`No page folders were configures.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersQuestionsSelectContentFolderQuickPickNoFoldersWarning)
);
return;
}
if (!selectedFolder && showWarning) {
Notifications.warning(`You didn't select a place where you wanted to create your content.`);
Notifications.warning(
l10n.t(LocalizationKey.helpersQuestionsSelectContentFolderQuickPickNoSelectionWarning)
);
return;
}
@@ -155,7 +170,9 @@ export class Questions {
): Promise<string | undefined> {
let contentTypes = ContentType.getAll();
if (!contentTypes || contentTypes.length === 0) {
Notifications.warning('No content types found. Please create a content type first.');
Notifications.warning(
l10n.t(LocalizationKey.helpersQuestionsSelectContentTypeNoContentTypeWarning)
);
return;
}
@@ -175,14 +192,16 @@ export class Questions {
}));
const selectedOption = await window.showQuickPick(options, {
title: `Content type`,
placeHolder: `Select the content type to create your new content`,
title: l10n.t(LocalizationKey.helpersQuestionsSelectContentTypeQuickPickTitle),
placeHolder: l10n.t(LocalizationKey.helpersQuestionsSelectContentTypeQuickPickPlaceholder),
canPickMany: false,
ignoreFocusOut: true
});
if (!selectedOption && showWarning) {
Notifications.warning('No content type was selected.');
Notifications.warning(
l10n.t(LocalizationKey.helpersQuestionsSelectContentTypeNoSelectionWarning)
);
return;
}

View File

@@ -1,6 +1,9 @@
import * as vscode from 'vscode';
import { ArticleHelper } from '.';
import { ParsedFrontMatter } from '../parsers';
import { EXTENSION_NAME } from '../constants';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class SeoHelper {
public static checkLength(
@@ -23,10 +26,15 @@ export class SeoHelper {
const diagnostic: vscode.Diagnostic = {
code: '',
message: `Article ${fieldName} is longer than ${length} characters (current length: ${value.length}). For SEO reasons, it would be better to make it less than ${length} characters.`,
message: l10n.t(
LocalizationKey.helpersSeoHelperCheckLengthDiagnosticMessage,
fieldName,
length,
value.length
),
range: new vscode.Range(posStart, posEnd),
severity: vscode.DiagnosticSeverity.Warning,
source: 'Front Matter'
source: EXTENSION_NAME
};
if (collection.has(editor.document.uri)) {

View File

@@ -51,6 +51,8 @@ import { GitListener } from '../listeners/general';
import { DataListener } from '../listeners/panel';
import { MarkdownFoldingProvider } from '../providers/MarkdownFoldingProvider';
import { ModeSwitch } from '../services/ModeSwitch';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class Settings {
public static globalFile = 'frontmatter.json';
@@ -181,16 +183,19 @@ export class Settings {
if (Settings.hasSettings()) {
window
.showInformationMessage(
`You have local settings. Would you like to promote them to the global settings ("frontmatter.json")?`,
'Yes',
'No'
l10n.t(LocalizationKey.helpersSettingsHelperCheckToPromoteMessage),
l10n.t(LocalizationKey.commonYes),
l10n.t(LocalizationKey.commonNo)
)
.then(async (result) => {
if (result === 'Yes') {
if (result === l10n.t(LocalizationKey.commonYes)) {
Settings.promote();
}
if (result === 'No' || result === 'Yes') {
if (
result === l10n.t(LocalizationKey.commonNo) ||
result === l10n.t(LocalizationKey.commonYes)
) {
Extension.getInstance().setState(ExtensionState.SettingPromoted, true, 'workspace');
}
});
@@ -529,7 +534,7 @@ export class Settings {
}
}
Notifications.info(`All settings promoted to team level.`);
Notifications.info(l10n.t(LocalizationKey.helpersSettingsHelperPromoteSuccess));
Telemetry.send(TelemetryEvent.promoteSettings);
}
@@ -663,7 +668,10 @@ export class Settings {
await window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: `${EXTENSION_NAME}: Reading dynamic config file...`
title: l10n.t(
LocalizationKey.helpersSettingsHelperReadConfigProgressTitle,
EXTENSION_NAME
)
},
async () => {
const absFilePath = Folders.getAbsFilePath(dynamicConfigPath);
@@ -691,9 +699,7 @@ export class Settings {
}
} catch (e) {
Settings.globalConfig = undefined;
Notifications.error(
`Error reading "frontmatter.json" config file. Check [output window](command:${COMMAND_NAME.showOutputChannel}) for more details.`
);
Notifications.errorWithOutput(l10n.t(LocalizationKey.helpersSettingsHelperReadConfigError));
Logger.error((e as Error).message);
}
@@ -1103,7 +1109,7 @@ export class Settings {
*/
private static async refreshConfig() {
await Settings.reloadConfig();
Notifications.info(`Settings have been refreshed.`);
Notifications.info(l10n.t(LocalizationKey.helpersSettingsHelperRefreshConfigSuccess));
}
/**

View File

@@ -64,6 +64,6 @@ export class SlugHelper {
*/
private static replaceCharacters(value: string) {
const characters = [...value];
return characters.map((c) => charMap[c] || c).join('');
return characters.map((c) => (typeof charMap[c] === 'string' ? charMap[c] : c)).join('');
}
}

View File

@@ -22,6 +22,8 @@ import { Folders } from '../commands';
import { join } from 'path';
import { SettingsListener as PanelSettingsListener } from '../listeners/panel';
import { SettingsListener as DashboardSettingsListener } from '../listeners/dashboard';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class TaxonomyHelper {
private static db: JsonDB;
@@ -126,15 +128,15 @@ export class TaxonomyHelper {
const { type, value } = data;
const answer = await window.showInputBox({
title: `Rename the "${value}"`,
title: l10n.t(LocalizationKey.helpersTaxonomyHelperRenameInputTitle, value),
value,
validateInput: (text) => {
if (text === value) {
return 'The new value must be different from the old one.';
return l10n.t(LocalizationKey.helpersTaxonomyHelperRenameValidateEqualValue);
}
if (!text) {
return 'A new value must be provided.';
return l10n.t(LocalizationKey.helpersTaxonomyHelperRenameValidateNoValue);
}
return null;
@@ -168,8 +170,8 @@ export class TaxonomyHelper {
const answer = await window.showQuickPick(
options.filter((o) => o !== value),
{
title: `Merge the "${value}" with another ${type} value`,
placeHolder: `Select the ${type} value to merge with`,
title: l10n.t(LocalizationKey.helpersTaxonomyHelperMergeQuickPickTitle, value, type),
placeHolder: l10n.t(LocalizationKey.helpersTaxonomyHelperMergeQuickPickPlaceholder, type),
ignoreFocusOut: true
}
);
@@ -188,13 +190,20 @@ export class TaxonomyHelper {
public static async delete(data: { type: string; value: string }) {
const { type, value } = data;
const answer = await window.showQuickPick(['Yes', 'No'], {
title: `Delete the "${value}" ${type} value`,
placeHolder: `Are you sure you want to delete the "${value}" ${type} value?`,
ignoreFocusOut: true
});
const answer = await window.showQuickPick(
[l10n.t(LocalizationKey.commonYes), l10n.t(LocalizationKey.commonNo)],
{
title: l10n.t(LocalizationKey.helpersTaxonomyHelperDeleteQuickPickTitle, value, type),
placeHolder: l10n.t(
LocalizationKey.helpersTaxonomyHelperDeleteQuickPickPlaceholder,
value,
type
),
ignoreFocusOut: true
}
);
if (!answer || answer === 'No') {
if (!answer || answer === l10n.t(LocalizationKey.commonNo)) {
return;
}
@@ -221,16 +230,16 @@ export class TaxonomyHelper {
const options = await this.getTaxonomyOptions(taxonomyType);
const newOption = await window.showInputBox({
title: `Create a new ${type} value`,
placeHolder: `The value you want to add`,
title: l10n.t(LocalizationKey.helpersTaxonomyHelperCreateNewInputTitle, type),
placeHolder: l10n.t(LocalizationKey.helpersTaxonomyHelperCreateNewInputPlaceholder),
ignoreFocusOut: true,
validateInput: (text) => {
if (!text) {
return 'A value must be provided.';
return l10n.t(LocalizationKey.helpersTaxonomyHelperCreateNewInputValidateNoValue);
}
if (options.includes(text)) {
return 'The value already exists.';
return l10n.t(LocalizationKey.helpersTaxonomyHelperCreateNewInputValidateExists);
}
return null;
@@ -276,11 +285,28 @@ export class TaxonomyHelper {
let progressText = ``;
if (type === 'edit') {
progressText = `${EXTENSION_NAME}: Renaming "${oldValue}" from ${taxonomyName} to "${newValue}".`;
progressText = l10n.t(
LocalizationKey.helpersTaxonomyHelperProcessEdit,
EXTENSION_NAME,
oldValue,
taxonomyName,
newValue || ''
);
} else if (type === 'merge') {
progressText = `${EXTENSION_NAME}: Merging "${oldValue}" from "${taxonomyName}" to "${newValue}".`;
progressText = l10n.t(
LocalizationKey.helpersTaxonomyHelperProcessMerge,
EXTENSION_NAME,
oldValue,
taxonomyName,
newValue || ''
);
} else if (type === 'delete') {
progressText = `${EXTENSION_NAME}: Deleting "${oldValue}" from "${taxonomyName}".`;
progressText = l10n.t(
LocalizationKey.helpersTaxonomyHelperProcessDelete,
EXTENSION_NAME,
oldValue,
taxonomyName
);
}
window.withProgress(
@@ -350,11 +376,11 @@ export class TaxonomyHelper {
await this.addToSettings(taxonomyType, oldValue, newValue);
if (type === 'edit') {
Notifications.info(`Edit completed.`);
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessEditSuccess));
} else if (type === 'merge') {
Notifications.info(`Merge completed.`);
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessMergeSuccess));
} else if (type === 'delete') {
Notifications.info(`Deletion completed.`);
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperProcessDeleteSuccess));
}
}
);
@@ -375,8 +401,8 @@ export class TaxonomyHelper {
options = options.filter((o) => o !== type);
const answer = await window.showQuickPick(options, {
title: `Move the "${value}" to another type`,
placeHolder: `Select the type to move to`,
title: l10n.t(LocalizationKey.helpersTaxonomyHelperMoveQuickPickTitle, value),
placeHolder: l10n.t(LocalizationKey.helpersTaxonomyHelperMoveQuickPickPlaceholder),
ignoreFocusOut: true
});
@@ -390,7 +416,13 @@ export class TaxonomyHelper {
window.withProgress(
{
location: ProgressLocation.Notification,
title: `${EXTENSION_NAME}: Moving "${value}" from ${type} to "${answer}".`,
title: l10n.t(
LocalizationKey.helpersTaxonomyHelperMoveProgressTitle,
EXTENSION_NAME,
value,
type,
answer
),
cancellable: false
},
async (progress) => {
@@ -465,7 +497,7 @@ export class TaxonomyHelper {
await this.process('delete', oldType, value);
Notifications.info(`Move completed.`);
Notifications.info(l10n.t(LocalizationKey.helpersTaxonomyHelperMoveSuccess));
}
);
}

View File

@@ -1,6 +1,8 @@
import { Uri, workspace, window } from 'vscode';
import { Logger } from './Logger';
import { Notifications } from './Notifications';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export const openFileInEditor = async (filePath: string) => {
if (filePath) {
@@ -8,7 +10,7 @@ export const openFileInEditor = async (filePath: string) => {
const doc = await workspace.openTextDocument(Uri.file(filePath));
await window.showTextDocument(doc, 1, false);
} catch (e) {
Notifications.error(`Couldn't open the file.`);
Notifications.error(l10n.t(LocalizationKey.helpersOpenFileInEditorError));
Logger.error(`${filePath}: ${(e as Error).message}`);
}
}

View File

@@ -1,3 +1,4 @@
import { dirname, relative } from 'path';
import { ContentFolder } from '../models';
export const processPathPlaceholders = (
@@ -7,6 +8,13 @@ export const processPathPlaceholders = (
contentFolder: ContentFolder | null | undefined
) => {
if (value && value.includes('{{pathToken.')) {
const relPathToken = '{{pathToken.relPath}}';
if (value.includes(relPathToken) && contentFolder?.path) {
const dirName = dirname(filePath);
let relPath = relative(contentFolder.path, dirName);
value = value.replace(relPathToken, relPath);
}
const regex = /{{pathToken.(\d+|relPath)}}/g;
const matches = value.match(regex);
if (matches) {

View File

@@ -1,6 +1,5 @@
import { Dashboard } from '../../commands/Dashboard';
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import { Logger } from '../../helpers/Logger';
import { PostMessageData } from '../../models';

View File

@@ -2,10 +2,14 @@ import { Dashboard } from '../../commands/Dashboard';
import { ExtensionState } from '../../constants';
import { DashboardCommand } from '../../dashboardWebView/DashboardCommand';
import { DashboardMessage } from '../../dashboardWebView/DashboardMessage';
import { Extension, Notifications } from '../../helpers';
import { Extension, Notifications, Settings } from '../../helpers';
import { PostMessageData } from '../../models';
import { PinnedItems } from '../../services';
import { BaseListener } from './BaseListener';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
import { Uri, commands } from 'vscode';
import { existsAsync } from '../../utils';
export class DashboardListener extends BaseListener {
/**
@@ -27,6 +31,9 @@ export class DashboardListener extends BaseListener {
case DashboardMessage.setPageViewType:
Extension.getInstance().setState(ExtensionState.PagesView, msg.payload, 'workspace');
break;
case DashboardMessage.openConfig:
this.openConfig();
break;
case DashboardMessage.showWarning:
Notifications.warning(msg.payload);
break;
@@ -42,6 +49,21 @@ export class DashboardListener extends BaseListener {
}
}
private static async openConfig() {
const answer = await Notifications.info(
l10n.t(LocalizationKey.listenersDashboardDashboardListenerOpenConfigNotification),
l10n.t(LocalizationKey.commonOpenSettings)
);
if (answer && answer === l10n.t(LocalizationKey.commonOpenSettings)) {
const configPath = await Settings.projectConfigPath();
if (configPath && (await existsAsync(configPath))) {
commands.executeCommand('vscode.open', Uri.file(configPath));
}
}
}
/**
* Get the pinned items
* @param msg
@@ -66,14 +88,22 @@ export class DashboardListener extends BaseListener {
const path = payload;
if (!path) {
this.sendError(command as any, requestId, 'No path provided.');
this.sendError(
command as any,
requestId,
l10n.t(LocalizationKey.listenersDashboardDashboardListenerPinItemNoPathError)
);
return;
}
const allPinned = await PinnedItems.pin(path);
if (!allPinned) {
this.sendError(command as any, requestId, 'Could not pin item.');
this.sendError(
command as any,
requestId,
l10n.t(LocalizationKey.listenersDashboardDashboardListenerPinItemCoundNotPinError)
);
return;
}
@@ -92,14 +122,22 @@ export class DashboardListener extends BaseListener {
const path = payload;
if (!path) {
this.sendError(command as any, requestId, 'No path provided.');
this.sendError(
command as any,
requestId,
l10n.t(LocalizationKey.listenersDashboardDashboardListenerPinItemNoPathError)
);
return;
}
const updatedPinned = await PinnedItems.remove(path);
if (!updatedPinned) {
this.sendError(command as any, requestId, 'Could not unpin item.');
this.sendError(
command as any,
requestId,
l10n.t(LocalizationKey.listenersDashboardDashboardListenerPinItemCoundNotUnPinError)
);
return;
}

View File

@@ -201,7 +201,9 @@ export class SettingsListener extends BaseListener {
await window.withProgress(
{
location: ProgressLocation.Notification,
title: 'Downloading and initializing the template...',
title: l10n.t(
LocalizationKey.listenersDashboardSettingsListenerTriggerTemplateProgressTitle
),
cancellable: false
},
async (progress) => {
@@ -209,7 +211,11 @@ export class SettingsListener extends BaseListener {
const ghFolder = await download(template.url, wsFolder.fsPath);
if (!ghFolder.downloaded) {
Notifications.error('Failed to download the template.');
Notifications.error(
l10n.t(
LocalizationKey.listenersDashboardSettingsListenerTriggerTemplateDownloadError
)
);
return;
}
@@ -225,7 +231,9 @@ export class SettingsListener extends BaseListener {
);
}
} catch (e) {
Notifications.error(`Failed to initialize the template.`);
Notifications.error(
l10n.t(LocalizationKey.listenersDashboardSettingsListenerTriggerTemplateInitError)
);
}
}
);

View File

@@ -7,6 +7,8 @@ import { Notifications, Settings, Telemetry } from '../../helpers';
import { PostMessageData, Snippets } from '../../models';
import { BaseListener } from './BaseListener';
import { SettingsListener } from './SettingsListener';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
export class SnippetListener extends BaseListener {
public static process(msg: PostMessageData) {
@@ -30,13 +32,17 @@ export class SnippetListener extends BaseListener {
const { title, description, body, fields, isMediaSnippet } = data;
if (!title || !body) {
Notifications.warning('Snippet missing title or body');
Notifications.warning(
l10n.t(LocalizationKey.listenersDashboardSnippetListenerAddSnippetMissingFieldsWarning)
);
return;
}
const snippets = Settings.get<any>(SETTING_CONTENT_SNIPPETS);
if (snippets && snippets[title]) {
Notifications.warning('Snippet with the same title already exists');
Notifications.warning(
l10n.t(LocalizationKey.listenersDashboardSnippetListenerAddSnippetExistsWarning)
);
return;
}
@@ -63,7 +69,9 @@ export class SnippetListener extends BaseListener {
const { snippets } = data;
if (!snippets) {
Notifications.warning('No snippets to update');
Notifications.warning(
l10n.t(LocalizationKey.listenersDashboardSnippetListenerUpdateSnippetNoSnippetsWarning)
);
return;
}

View File

@@ -5,10 +5,15 @@ import { BaseListener } from './BaseListener';
import { exec } from 'child_process';
import { Extension, Logger, Settings } from '../../helpers';
import { Folders } from '../../commands';
import { SETTING_TAXONOMY_CONTENT_TYPES, SsgScripts } from '../../constants';
import {
DEFAULT_CONTENT_TYPE_NAME,
SETTING_TAXONOMY_CONTENT_TYPES,
SsgScripts
} from '../../constants';
import { SettingsListener } from './SettingsListener';
import { Terminal } from '../../services';
import { existsAsync, readFileAsync } from '../../utils';
import { join } from 'path';
export class SsgListener extends BaseListener {
/**
@@ -57,7 +62,16 @@ export class SsgListener extends BaseListener {
}
}
const contentTypes = Settings.get<ContentType[]>(SETTING_TAXONOMY_CONTENT_TYPES) || [];
// Set the preview image on the first found image of the content type
const images = contentType.fields.filter((f) => f.type === 'image');
if (images.length > 0) {
images[0].isPreviewImage = true;
}
let contentTypes = Settings.get<ContentType[]>(SETTING_TAXONOMY_CONTENT_TYPES) || [];
// Filter out the default content type
contentTypes = contentTypes.filter((ct) => ct.name !== DEFAULT_CONTENT_TYPE_NAME);
if (contentTypes.find((ct) => ct.name === collection.name)) {
SsgListener.sendRequest(command as any, requestId, {});
@@ -133,7 +147,28 @@ export class SsgListener extends BaseListener {
const tempLocation = Uri.joinPath(wsFolder, '/.frontmatter/temp');
const tempScriptPath = Uri.joinPath(tempLocation, SsgScripts.astroContentCollectionScript);
await workspace.fs.createDirectory(tempLocation);
workspace.fs.copy(scriptPath, tempScriptPath, { overwrite: true });
// Check if the workspace uses pnpm
if (await existsAsync(Uri.joinPath(wsFolder, 'node_modules/.pnpm').fsPath)) {
const vitePackageFiles = await workspace.findFiles(
`**/node_modules/.pnpm/vite@*/node_modules/vite/package.json`
);
if (vitePackageFiles.length > 0) {
const vitePackageFile = vitePackageFiles[0];
const vitePackage = JSON.parse(await readFileAsync(vitePackageFile.fsPath, 'utf8')) as {
main: string;
};
const viteFolder = vitePackageFile.fsPath.replace('/package.json', '');
const vitePath = join(viteFolder, vitePackage.main).replace(wsFolder.fsPath, '../..');
// Update the vite reference, as it is not a direct dependency of the project
let scriptContents = await readFileAsync(scriptPath.fsPath, 'utf8');
scriptContents = scriptContents.replace(`'vite'`, `'${vitePath}'`);
await workspace.fs.writeFile(tempScriptPath, Buffer.from(scriptContents, 'utf8'));
}
} else {
workspace.fs.copy(scriptPath, tempScriptPath, { overwrite: true });
}
const fullScript = `node "${tempScriptPath.fsPath}" "${contentConfigFile.fsPath}"`;
@@ -187,16 +222,35 @@ export class SsgListener extends BaseListener {
} as Field;
break;
case 'ZodBoolean':
ctField = {
name: field.name,
type: 'boolean'
} as Field;
if (field.name === 'draft') {
ctField = {
name: field.name,
type: 'draft'
} as Field;
} else {
ctField = {
name: field.name,
type: 'boolean'
} as Field;
}
break;
case 'ZodArray':
ctField = {
name: field.name,
type: 'list'
} as Field;
if (field.name === 'tags') {
ctField = {
name: field.name,
type: 'tags'
} as Field;
} else if (field.name === 'categories') {
ctField = {
name: field.name,
type: 'categories'
} as Field;
} else {
ctField = {
name: field.name,
type: 'list'
} as Field;
}
break;
case 'ZodEnum':
ctField = {
@@ -205,12 +259,23 @@ export class SsgListener extends BaseListener {
choices: field.options || []
} as Field;
break;
case 'datetime':
case 'ZodDate':
ctField = {
name: field.name,
type: 'datetime',
default: '{{now}}'
} as Field;
if (field.name.toLowerCase() === 'published') {
ctField.isPublishDate = true;
} else if (
field.name.toLowerCase() === 'modified' ||
field.name.toLowerCase() === 'updated'
) {
ctField.isModifiedDate = true;
}
break;
case 'image':
ctField = {

View File

@@ -28,6 +28,8 @@ import {
import { Folders } from '../../commands/Folders';
import { commands } from 'vscode';
import { PostMessageData } from '../../models';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
export class GitListener {
private static isRegistered: boolean = false;
@@ -177,7 +179,9 @@ export class GitListener {
}
await subGit.push();
} catch (e) {
Notifications.error(`Failed to push submodules. Please check the logs for more details.`);
Notifications.errorWithOutput(
l10n.t(LocalizationKey.listenersGeneralGitListenerPushError)
);
Logger.error((e as Error).message);
return;
}
@@ -203,7 +207,9 @@ export class GitListener {
await git.subModule(['foreach', 'git', 'push']);
}
} catch (e) {
Notifications.error(`Failed to push submodules. Please check the logs for more details.`);
Notifications.errorWithOutput(
l10n.t(LocalizationKey.listenersGeneralGitListenerPushError)
);
Logger.error((e as Error).message);
return;
}

View File

@@ -19,11 +19,13 @@ import { Article, Preview } from '../../commands';
import { ParsedFrontMatter } from '../../parsers';
import { processKnownPlaceholders } from '../../helpers/PlaceholderHelper';
import { Field, PostMessageData } from '../../models';
import { encodeEmoji } from '../../utils';
import { encodeEmoji, fieldWhenClause } from '../../utils';
import { PanelProvider } from '../../panelWebView/PanelProvider';
import { MessageHandlerData } from '@estruyf/vscode';
import { SponsorAi } from '../../services/SponsorAI';
import { Terminal } from '../../services';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
const FILE_LIMIT = 10;
@@ -94,7 +96,7 @@ export class DataListener extends BaseListener {
panel.getWebview()?.postMessage({
command,
requestId,
error: 'No active editor'
error: l10n.t(LocalizationKey.listenersPanelDataListenerAiSuggestTaxonomyNoEditorError)
} as MessageHandlerData<string>);
return;
}
@@ -104,7 +106,7 @@ export class DataListener extends BaseListener {
panel.getWebview()?.postMessage({
command,
requestId,
error: 'No article data'
error: l10n.t(LocalizationKey.listenersPanelDataListenerAiSuggestTaxonomyNoDataError)
} as MessageHandlerData<string>);
return;
}
@@ -126,7 +128,7 @@ export class DataListener extends BaseListener {
panel.getWebview()?.postMessage({
command,
requestId,
error: 'No article data'
error: l10n.t(LocalizationKey.listenersPanelDataListenerAiSuggestTaxonomyNoDataError)
} as MessageHandlerData<string>);
return;
}
@@ -255,9 +257,18 @@ export class DataListener extends BaseListener {
}
const contentType = ArticleHelper.getContentType(article);
const sourceField = ContentType.findFieldByName(contentType.fields, field);
if (!value && field !== titleField && contentType.clearEmpty) {
value = undefined;
// Check if the draft or boolean field needs to be cleared
// This is only required when the default value is not set to true
if (sourceField && (sourceField.type === 'draft' || sourceField.type === 'boolean')) {
if (!sourceField.default) {
value = undefined;
}
} else {
value = undefined;
}
}
const dateFields = ContentType.findFieldsByTypeDeep(contentType.fields, 'datetime');
@@ -337,6 +348,29 @@ export class DataListener extends BaseListener {
}
}
// Verify if there are fields to be cleared due to the when clause
const allFieldNames = Object.keys(parentObj);
let ctFields = contentType.fields;
if (parents && parents.length > 0) {
for (const parent of parents) {
const crntField = ctFields.find((f) => f.name === parent);
if (crntField) {
ctFields = crntField.fields || [];
}
}
}
if (ctFields && ctFields.length > 0) {
for (const field of allFieldNames) {
const crntField = ctFields.find((f) => f.name === field);
if (crntField && crntField.when) {
const renderField = fieldWhenClause(crntField, parentObj, ctFields);
if (!renderField) {
delete parentObj[field];
}
}
}
}
// Clear the field if it is empty
if (
value === undefined ||
@@ -499,7 +533,11 @@ export class DataListener extends BaseListener {
if (entries) {
this.sendRequest(command, requestId, entries);
} else {
this.sendRequestError(command, requestId, "Couldn't find data file entries");
this.sendRequestError(
command,
requestId,
l10n.t(LocalizationKey.listenersPanelDataListenerGetDataFileEntriesNoDataFilesError)
);
}
}

View File

@@ -13,6 +13,8 @@ import {
import { SponsorAi } from '../../services/SponsorAI';
import { PanelProvider } from '../../panelWebView/PanelProvider';
import { MessageHandlerData } from '@estruyf/vscode';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
export class TaxonomyListener extends BaseListener {
/**
@@ -80,7 +82,7 @@ export class TaxonomyListener extends BaseListener {
panel.getWebview()?.postMessage({
command,
requestId,
error: 'No active editor'
error: l10n.t(LocalizationKey.listenersPanelTaxonomyListenerAiSuggestTaxonomyNoDataError)
} as MessageHandlerData<string>);
return;
}
@@ -90,7 +92,7 @@ export class TaxonomyListener extends BaseListener {
panel.getWebview()?.postMessage({
command,
requestId,
error: 'No article data'
error: l10n.t(LocalizationKey.listenersPanelTaxonomyListenerAiSuggestTaxonomyNoEditorError)
} as MessageHandlerData<string>);
return;
}
@@ -115,7 +117,7 @@ export class TaxonomyListener extends BaseListener {
panel.getWebview()?.postMessage({
command,
requestId,
error: 'No article data'
error: l10n.t(LocalizationKey.listenersPanelTaxonomyListenerAiSuggestTaxonomyNoDataError)
} as MessageHandlerData<string>);
return;
}

View File

@@ -115,6 +115,30 @@ export enum LocalizationKey {
* No results
*/
commonNoResults = 'common.noResults',
/**
* Sorry, something went wrong.
*/
commonError = 'common.error',
/**
* yes
*/
commonYes = 'common.yes',
/**
* no
*/
commonNo = 'common.no',
/**
* Open settings
*/
commonOpenSettings = 'common.openSettings',
/**
* output window
*/
notificationsOutputChannelLink = 'notifications.outputChannel.link',
/**
* Check the {0} for more details.
*/
notificationsOutputChannelDescription = 'notifications.outputChannel.description',
/**
* Common
*/
@@ -891,6 +915,10 @@ export enum LocalizationKey {
* Select a template to prefill the frontmatter.json file with the recommended settings.
*/
dashboardStepsStepsToGetStartedTemplateDescription = 'dashboard.steps.stepsToGetStarted.template.description',
/**
* Selecting a template applies a whole configuration to your project and closes this configuration view.
*/
dashboardStepsStepsToGetStartedTemplateWarning = 'dashboard.steps.stepsToGetStarted.template.warning',
/**
* Create Content-Types for your Astro Content Collections
*/
@@ -1032,7 +1060,7 @@ export enum LocalizationKey {
*/
dashboardConfigurationAstroAstroContentTypesEmpty = 'dashboard.configuration.astro.astroContentTypes.empty',
/**
* The following Astro Content Collections and can be used to generate a content-type.
* The following Astro Content Collections can be used to generate a content-type.
*/
dashboardConfigurationAstroAstroContentTypesDescription = 'dashboard.configuration.astro.astroContentTypes.description',
/**
@@ -1452,8 +1480,852 @@ export enum LocalizationKey {
* Continue in the media dashboard to select the image you want to insert.
*/
panelViewPanelMediaInsert = 'panel.viewPanel.mediaInsert',
/**
* No {0} configured.
*/
commandsArticleNotificationNoTaxonomy = 'commands.article.notification.noTaxonomy',
/**
* Select your {0} to insert.
*/
commandsArticleQuickPickPlaceholder = 'commands.article.quickPick.placeholder',
/**
* Something failed while parsing the date format. Check your "{0}" setting.
*/
commandsArticleSetDateError = 'commands.article.setDate.error',
/**
* Failed to rename file: {0}
*/
commandsArticleUpdateSlugError = 'commands.article.updateSlug.error',
/**
* Cache cleared
*/
commandsCacheCleared = 'commands.cache.cleared',
/**
* Ask me anything
*/
commandsChatbotTitle = 'commands.chatbot.title',
/**
* Create content by content type
*/
commandsContentOptionContentTypeLabel = 'commands.content.option.contentType.label',
/**
* Select if you want to create new content by the available content type(s)
*/
commandsContentOptionContentTypeDescription = 'commands.content.option.contentType.description',
/**
* Create content by template
*/
commandsContentOptionTemplateLabel = 'commands.content.option.template.label',
/**
* Select if you want to create new content by the available template(s)
*/
commandsContentOptionTemplateDescription = 'commands.content.option.template.description',
/**
* Create content
*/
commandsContentQuickPickTitle = 'commands.content.quickPick.title',
/**
* Select how you want to create your new content
*/
commandsContentQuickPickPlaceholder = 'commands.content.quickPick.placeholder',
/**
* Dashboard
*/
commandsDashboardTitle = 'commands.dashboard.title',
/**
* Add media folder
*/
commandsFoldersAddMediaFolderInputBoxTitle = 'commands.folders.addMediaFolder.inputBox.title',
/**
* Which name would you like to give to your folder (use "/" to create multi-level folders)?
*/
commandsFoldersAddMediaFolderInputBoxPrompt = 'commands.folders.addMediaFolder.inputBox.prompt',
/**
* No folder name was specified.
*/
commandsFoldersAddMediaFolderNoFolderWarning = 'commands.folders.addMediaFolder.noFolder.warning',
/**
* Folder is already registered
*/
commandsFoldersCreateFolderExistsWarning = 'commands.folders.create.folderExists.warning',
/**
* Register folder
*/
commandsFoldersCreateInputTitle = 'commands.folders.create.input.title',
/**
* Which name would you like to specify for this folder?
*/
commandsFoldersCreateInputPrompt = 'commands.folders.create.input.prompt',
/**
* Folder name
*/
commandsFoldersCreateInputPlaceholder = 'commands.folders.create.input.placeholder',
/**
* Folder registered
*/
commandsFoldersCreateSuccess = 'commands.folders.create.success',
/**
* Please select the main workspace folder for Front Matter to use.
*/
commandsFoldersGetWorkspaceFolderWorkspaceFolderPickPlaceholder = 'commands.folders.getWorkspaceFolder.workspaceFolderPick.placeholder',
/**
* Folder "{0}" does not exist. Please remove it from the settings.
*/
commandsFoldersGetNotificationErrorTitle = 'commands.folders.get.notificationError.title',
/**
* Remove folder
*/
commandsFoldersGetNotificationErrorRemoveAction = 'commands.folders.get.notificationError.remove.action',
/**
* Create folder
*/
commandsFoldersGetNotificationErrorCreateAction = 'commands.folders.get.notificationError.create.action',
/**
* Preview: {0}
*/
commandsPreviewPanelTitle = 'commands.preview.panel.title',
/**
* Select the folder of the article to preview
*/
commandsPreviewAskUserToPickFolderTitle = 'commands.preview.askUserToPickFolder.title',
/**
* Project initialized successfully.
*/
commandsProjectInitializeSuccess = 'commands.project.initialize.success',
/**
* To which project do you want to switch?
*/
commandsProjectSwitchProjectTitle = 'commands.project.switchProject.title',
/**
* Sample template created.
*/
commandsProjectCreateSampleTemplateInfo = 'commands.project.createSampleTemplate.info',
/**
* Insert the value of the {0} that you want to add to your configuration.
*/
commandsSettingsCreateInputPrompt = 'commands.settings.create.input.prompt',
/**
* Name of the {0}
*/
commandsSettingsCreateInputPlaceholder = 'commands.settings.create.input.placeholder',
/**
* The provided {0} already exists.
*/
commandsSettingsCreateWarning = 'commands.settings.create.warning',
/**
* Do you want to add the new {0} to the page?
*/
commandsSettingsCreateQuickPickPlaceholder = 'commands.settings.create.quickPick.placeholder',
/**
* {0}: exporting tags and categories
*/
commandsSettingsExportProgressTitle = 'commands.settings.export.progress.title',
/**
* Export completed. Tags: {0} - Categories: {1}.
*/
commandsSettingsExportProgressSuccess = 'commands.settings.export.progress.success',
/**
* Remap
*/
commandsSettingsRemapQuickpickTitle = 'commands.settings.remap.quickpick.title',
/**
* What do you want to remap?
*/
commandsSettingsRemapQuickpickPlaceholder = 'commands.settings.remap.quickpick.placeholder',
/**
* No {0} configured.
*/
commandsSettingsRemapNoTaxonomyWarning = 'commands.settings.remap.noTaxonomy.warning',
/**
* Select your {0} to insert.
*/
commandsSettingsRemapSelectTaxonomyPlaceholder = 'commands.settings.remap.selectTaxonomy.placeholder',
/**
* Specify the value of the {0} with which you want to remap "{1}". Leave the input <blank> if you want to remove the {0} from all articles.
*/
commandsSettingsRemapNewOptionInputPrompt = 'commands.settings.remap.newOption.input.prompt',
/**
* Name of the {0}
*/
commandsSettingsRemapNewOptionInputPlaceholder = 'commands.settings.remap.newOption.input.placeholder',
/**
* Delete {0} {1}?
*/
commandsSettingsRemapDeletePlaceholder = 'commands.settings.remap.delete.placeholder',
/**
* The {0} field is required. Please define a value for the field.
*/
commandsStatusListenerVerifyRequiredFieldsDiagnosticEmptyField = 'commands.statusListener.verifyRequiredFields.diagnostic.emptyField',
/**
* The following fields are required to contain a value: {0}
*/
commandsStatusListenerVerifyRequiredFieldsNotificationError = 'commands.statusListener.verifyRequiredFields.notification.error',
/**
* Template title
*/
commandsTemplateGenerateInputTitle = 'commands.template.generate.input.title',
/**
* Which name would you like to give your template?
*/
commandsTemplateGenerateInputPrompt = 'commands.template.generate.input.prompt',
/**
* article
*/
commandsTemplateGenerateInputPlaceholder = 'commands.template.generate.input.placeholder',
/**
* You did not specify a template title.
*/
commandsTemplateGenerateNoTitleWarning = 'commands.template.generate.noTitle.warning',
/**
* Keep content
*/
commandsTemplateGenerateKeepContentsTitle = 'commands.template.generate.keepContents.title',
/**
* Do you want to keep the contents for the template?
*/
commandsTemplateGenerateKeepContentsPlaceholder = 'commands.template.generate.keepContents.placeholder',
/**
* You did not pick any of the options for keeping the template its content.
*/
commandsTemplateGenerateKeepContentsNoOptionWarning = 'commands.template.generate.keepContents.noOption.warning',
/**
* Template created and is now available in your {0} folder.
*/
commandsTemplateGenerateKeepContentsSuccess = 'commands.template.generate.keepContents.success',
/**
* No templates found.
*/
commandsTemplateGetTemplatesWarning = 'commands.template.getTemplates.warning',
/**
* Incorrect project folder path retrieved.
*/
commandsTemplateCreateFolderPathWarning = 'commands.template.create.folderPath.warning',
/**
* No templates found.
*/
commandsTemplateCreateNoTemplatesWarning = 'commands.template.create.noTemplates.warning',
/**
* Select a template
*/
commandsTemplateCreateSelectTemplateTitle = 'commands.template.create.selectTemplate.title',
/**
* Select the content template to use
*/
commandsTemplateCreateSelectTemplatePlaceholder = 'commands.template.create.selectTemplate.placeholder',
/**
* No template selected.
*/
commandsTemplateCreateSelectTemplateNoTemplateWarning = 'commands.template.create.selectTemplate.noTemplate.warning',
/**
* Content template could not be found.
*/
commandsTemplateCreateSelectTemplateNotFoundWarning = 'commands.template.create.selectTemplate.notFound.warning',
/**
* Your new content is now available.
*/
commandsTemplateCreateSuccess = 'commands.template.create.success',
/**
* Unordered list
*/
commandsWysiwygCommandUnorderedListLabel = 'commands.wysiwyg.command.unorderedList.label',
/**
* Add an unordered list
*/
commandsWysiwygCommandUnorderedListDetail = 'commands.wysiwyg.command.unorderedList.detail',
/**
* Ordered list
*/
commandsWysiwygCommandOrderedListLabel = 'commands.wysiwyg.command.orderedList.label',
/**
* Add an ordered list
*/
commandsWysiwygCommandOrderedListDetail = 'commands.wysiwyg.command.orderedList.detail',
/**
* Task list
*/
commandsWysiwygCommandTaskListLabel = 'commands.wysiwyg.command.taskList.label',
/**
* Add a task list
*/
commandsWysiwygCommandTaskListDetail = 'commands.wysiwyg.command.taskList.detail',
/**
* Code
*/
commandsWysiwygCommandCodeLabel = 'commands.wysiwyg.command.code.label',
/**
* Add inline code snippet
*/
commandsWysiwygCommandCodeDetail = 'commands.wysiwyg.command.code.detail',
/**
* Code block
*/
commandsWysiwygCommandCodeblockLabel = 'commands.wysiwyg.command.codeblock.label',
/**
* Add a code block
*/
commandsWysiwygCommandCodeblockDetail = 'commands.wysiwyg.command.codeblock.detail',
/**
* Blockquote
*/
commandsWysiwygCommandBlockquoteLabel = 'commands.wysiwyg.command.blockquote.label',
/**
* Add a blockquote
*/
commandsWysiwygCommandBlockquoteDetail = 'commands.wysiwyg.command.blockquote.detail',
/**
* Strikethrough
*/
commandsWysiwygCommandStrikethroughLabel = 'commands.wysiwyg.command.strikethrough.label',
/**
* Add strikethrough text
*/
commandsWysiwygCommandStrikethroughDetail = 'commands.wysiwyg.command.strikethrough.detail',
/**
* WYSIWYG Options
*/
commandsWysiwygQuickPickTitle = 'commands.wysiwyg.quickPick.title',
/**
* Which type of markup would you like to insert?
*/
commandsWysiwygQuickPickPlaceholder = 'commands.wysiwyg.quickPick.placeholder',
/**
* WYSIWYG Hyperlink
*/
commandsWysiwygAddHyperlinkHyperlinkInputTitle = 'commands.wysiwyg.addHyperlink.hyperlinkInput.title',
/**
* Enter the URL
*/
commandsWysiwygAddHyperlinkHyperlinkInputPrompt = 'commands.wysiwyg.addHyperlink.hyperlinkInput.prompt',
/**
* WYSIWYG Text
*/
commandsWysiwygAddHyperlinkTextInputTitle = 'commands.wysiwyg.addHyperlink.textInput.title',
/**
* Enter the text for the hyperlink
*/
commandsWysiwygAddHyperlinkTextInputPrompt = 'commands.wysiwyg.addHyperlink.textInput.prompt',
/**
* Heading level
*/
commandsWysiwygInsertTextHeadingInputTitle = 'commands.wysiwyg.insertText.heading.input.title',
/**
* Which heading level do you want to insert?
*/
commandsWysiwygInsertTextHeadingInputPlaceholder = 'commands.wysiwyg.insertText.heading.input.placeholder',
/**
* A page bundle with the name {0} already exists in {1}.
*/
helpersArticleHelperCreateContentPageBundleError = 'helpers.articleHelper.createContent.pageBundle.error',
/**
* Content with the title already exists. Please specify a new title.
*/
helpersArticleHelperCreateContentContentExistsWarning = 'helpers.articleHelper.createContent.contentExists.warning',
/**
* Error while processing the {0} placeholder.
*/
helpersArticleHelperProcessCustomPlaceholdersPlaceholderError = 'helpers.articleHelper.processCustomPlaceholders.placeholder.error',
/**
* Error parsing the front matter of {0}.
*/
helpersArticleHelperParseFileDiagnosticError = 'helpers.articleHelper.parseFile.diagnostic.error',
/**
* No front matter data found to generate a content type.
*/
helpersContentTypeGenerateNoFrontMatterError = 'helpers.contentType.generate.noFrontMatter.error',
/**
* Override the default content type
*/
helpersContentTypeGenerateOverrideQuickPickTitle = 'helpers.contentType.generate.override.quickPick.title',
/**
* Do you want to overwrite the default content type configuration with the fields used in the current field?
*/
helpersContentTypeGenerateOverrideQuickPickPlaceholder = 'helpers.contentType.generate.override.quickPick.placeholder',
/**
* Generate Content Type
*/
helpersContentTypeGenerateContentTypeInputTitle = 'helpers.contentType.generate.contentTypeInput.title',
/**
* Enter the name of the content type to generate
*/
helpersContentTypeGenerateContentTypeInputPrompt = 'helpers.contentType.generate.contentTypeInput.prompt',
/**
* Please enter a name for the content type.
*/
helpersContentTypeGenerateContentTypeInputValidationEnterName = 'helpers.contentType.generate.contentTypeInput.validation.enterName',
/**
* A content type with this name already exists.
*/
helpersContentTypeGenerateContentTypeInputValidationNameExists = 'helpers.contentType.generate.contentTypeInput.validation.nameExists',
/**
* You didn't specify a name for the content type.
*/
helpersContentTypeGenerateNoContentTypeNameWarning = 'helpers.contentType.generate.noContentTypeName.warning',
/**
* Use as a page bundle
*/
helpersContentTypeGeneratePageBundleQuickPickTitle = 'helpers.contentType.generate.pageBundle.quickPick.title',
/**
* Do you want to use this content type as a page bundle?
*/
helpersContentTypeGeneratePageBundleQuickPickPlaceHolder = 'helpers.contentType.generate.pageBundle.quickPick.placeHolder',
/**
* Content type {0} has been updated.
*/
helpersContentTypeGenerateUpdatedSuccess = 'helpers.contentType.generate.updated.success',
/**
* Content type {0} has been generated.
*/
helpersContentTypeGenerateGeneratedSuccess = 'helpers.contentType.generate.generated.success',
/**
* No front matter data found to add missing fields.
*/
helpersContentTypeAddMissingFieldsNoFrontMatterWarning = 'helpers.contentType.addMissingFields.noFrontMatter.warning',
/**
* Content type {0} has been updated.
*/
helpersContentTypeAddMissingFieldsUpdatedSuccess = 'helpers.contentType.addMissingFields.updated.success',
/**
* No front matter data found to set the content type.
*/
helpersContentTypeSetContentTypeNoFrontMatterWarning = 'helpers.contentType.setContentType.noFrontMatter.warning',
/**
* Select the content type
*/
helpersContentTypeSetContentTypeQuickPickTitle = 'helpers.contentType.setContentType.quickPick.title',
/**
* Which content type would you like to use?
*/
helpersContentTypeSetContentTypeQuickPickPlaceholder = 'helpers.contentType.setContentType.quickPick.placeholder',
/**
* Do you want to create it as sub-content?
*/
helpersContentTypeCreateAllowSubContentTitle = 'helpers.contentType.create.allowSubContent.title',
/**
* Do you want to create it as sub-content?
*/
helpersContentTypeCreateAllowSubContentPlaceHolder = 'helpers.contentType.create.allowSubContent.placeHolder',
/**
* Select folder
*/
helpersContentTypeCreateAllowSubContentShowOpenDialogOpenLabel = 'helpers.contentType.create.allowSubContent.showOpenDialog.openLabel',
/**
* Select folder to create the content
*/
helpersContentTypeCreateAllowSubContentShowOpenDialogTitle = 'helpers.contentType.create.allowSubContent.showOpenDialog.title',
/**
* Create as a page bundle?
*/
helpersContentTypeCreatePageBundleTitle = 'helpers.contentType.create.pageBundle.title',
/**
* Do you want to create the sub-content as a page bundle?
*/
helpersContentTypeCreatePageBundlePlaceHolder = 'helpers.contentType.create.pageBundle.placeHolder',
/**
* {0}: Creating content...
*/
helpersContentTypeCreateProgressTitle = 'helpers.contentType.create.progress.title',
/**
* Your new content has been created.
*/
helpersContentTypeCreateSuccess = 'helpers.contentType.create.success',
/**
* The content type actions are not available in this mode.
*/
helpersContentTypeVerifyWarning = 'helpers.contentType.verify.warning',
/**
* Executing: {0}
*/
helpersCustomScriptExecuting = 'helpers.customScript.executing',
/**
* {0}: Article couldn't be retrieved.
*/
helpersCustomScriptSingleRunArticleWarning = 'helpers.customScript.singleRun.article.warning',
/**
* {0}: No files found
*/
helpersCustomScriptBulkRunNoFilesWarning = 'helpers.customScript.bulkRun.noFiles.warning',
/**
* {0}: There was no folder or media path specified.
*/
helpersCustomScriptRunMediaScriptNoFolderWarning = 'helpers.customScript.runMediaScript.noFolder.warning',
/**
* {0}: front matter updated.
*/
helpersCustomScriptShowOutputFrontMatterSuccess = 'helpers.customScript.showOutput.frontMatter.success',
/**
* Copy output
*/
helpersCustomScriptShowOutputCopyOutputAction = 'helpers.customScript.showOutput.copyOutput.action',
/**
* {0}: Executed your custom script.
*/
helpersCustomScriptShowOutputSuccess = 'helpers.customScript.showOutput.success',
/**
* Invalid command: {0}
*/
helpersCustomScriptValidateCommandError = 'helpers.customScript.validateCommand.error',
/**
* Something went wrong while processing the data file.
*/
helpersDataFileHelperProcessError = 'helpers.dataFileHelper.process.error',
/**
* Check the changelog
*/
helpersExtensionGetVersionChangelog = 'helpers.extension.getVersion.changelog',
/**
* Give it a ⭐️
*/
helpersExtensionGetVersionStarIt = 'helpers.extension.getVersion.starIt',
/**
* {0} has been updated to v{1} — check out what's new!
*/
helpersExtensionGetVersionUpdateNotification = 'helpers.extension.getVersion.update.notification',
/**
* The "{0}" and "{1}" settings have been deprecated. Please use the "isPublishDate" and "isModifiedDate" datetime field properties instead.
*/
helpersExtensionMigrateSettingsDeprecatedWarning = 'helpers.extension.migrateSettings.deprecated.warning',
/**
* Hide
*/
helpersExtensionMigrateSettingsDeprecatedWarningHide = 'helpers.extension.migrateSettings.deprecated.warning.hide',
/**
* See migration guide
*/
helpersExtensionMigrateSettingsDeprecatedWarningSeeGuide = 'helpers.extension.migrateSettings.deprecated.warning.seeGuide',
/**
* {0} - Templates
*/
helpersExtensionMigrateSettingsTemplatesQuickPickTitle = 'helpers.extension.migrateSettings.templates.quickPick.title',
/**
* Do you want to keep on using the template functionality?
*/
helpersExtensionMigrateSettingsTemplatesQuickPickPlaceholder = 'helpers.extension.migrateSettings.templates.quickPick.placeholder',
/**
* Front Matter BETA cannot be used while the stable version is installed. Please ensure that you have only over version installed.
*/
helpersExtensionCheckIfExtensionCanRunWarning = 'helpers.extension.checkIfExtensionCanRun.warning',
/**
* We couldn't find your selected folder.
*/
helpersMediaHelperSaveFileFolderError = 'helpers.mediaHelper.saveFile.folder.error',
/**
* File {0} uploaded to: {1}
*/
helpersMediaHelperSaveFileFileUploadedSuccess = 'helpers.mediaHelper.saveFile.file.uploaded.success',
/**
* Sorry, something went wrong uploading {0}
*/
helpersMediaHelperSaveFileFileUploadedFailed = 'helpers.mediaHelper.saveFile.file.uploaded.failed',
/**
* Sorry, something went wrong deleting {0}
*/
helpersMediaHelperDeleteFileFileDeletionFailed = 'helpers.mediaHelper.deleteFile.file.deletion.failed',
/**
* The name "{0}" already exists at the file location.
*/
helpersMediaLibraryRemoveWarning = 'helpers.mediaLibrary.remove.warning',
/**
* Sorry, something went wrong updating "{0}" to "{1}".
*/
helpersMediaLibraryRemoveError = 'helpers.mediaLibrary.remove.error',
/**
* Couldn't open the file.
*/
helpersOpenFileInEditorError = 'helpers.openFileInEditor.error',
/**
* Title or description
*/
helpersQuestionsContentTitleAiInputTitle = 'helpers.questions.contentTitle.aiInput.title',
/**
* What would you like to write about?
*/
helpersQuestionsContentTitleAiInputPrompt = 'helpers.questions.contentTitle.aiInput.prompt',
/**
* What would you like to write about?
*/
helpersQuestionsContentTitleAiInputPlaceholder = 'helpers.questions.contentTitle.aiInput.placeholder',
/**
* your title/description
*/
helpersQuestionsContentTitleAiInputQuickPickTitleSeparator = 'helpers.questions.contentTitle.aiInput.quickPick.title.separator',
/**
* AI generated title
*/
helpersQuestionsContentTitleAiInputQuickPickAiSeparator = 'helpers.questions.contentTitle.aiInput.quickPick.ai.separator',
/**
* Select a title
*/
helpersQuestionsContentTitleAiInputSelectTitle = 'helpers.questions.contentTitle.aiInput.select.title',
/**
* Select a title for your content
*/
helpersQuestionsContentTitleAiInputSelectPlaceholder = 'helpers.questions.contentTitle.aiInput.select.placeholder',
/**
* Failed fetching the AI title. Please try to use your own title or try again later.
*/
helpersQuestionsContentTitleAiInputFailed = 'helpers.questions.contentTitle.aiInput.failed',
/**
* You did not specify a title for your content.
*/
helpersQuestionsContentTitleAiInputWarning = 'helpers.questions.contentTitle.aiInput.warning',
/**
* Content title
*/
helpersQuestionsContentTitleTitleInputTitle = 'helpers.questions.contentTitle.titleInput.title',
/**
* What would you like to use as a title for the content to create?
*/
helpersQuestionsContentTitleTitleInputPrompt = 'helpers.questions.contentTitle.titleInput.prompt',
/**
* Content title
*/
helpersQuestionsContentTitleTitleInputPlaceholder = 'helpers.questions.contentTitle.titleInput.placeholder',
/**
* You did not specify a title for your content.
*/
helpersQuestionsContentTitleTitleInputWarning = 'helpers.questions.contentTitle.titleInput.warning',
/**
* Select a folder
*/
helpersQuestionsSelectContentFolderQuickPickTitle = 'helpers.questions.selectContentFolder.quickPick.title',
/**
* Select where you want to create your content
*/
helpersQuestionsSelectContentFolderQuickPickPlaceholder = 'helpers.questions.selectContentFolder.quickPick.placeholder',
/**
* No page folders were configured.
*/
helpersQuestionsSelectContentFolderQuickPickNoFoldersWarning = 'helpers.questions.selectContentFolder.quickPick.noFolders.warning',
/**
* You didn't select a place where you wanted to create your content.
*/
helpersQuestionsSelectContentFolderQuickPickNoSelectionWarning = 'helpers.questions.selectContentFolder.quickPick.noSelection.warning',
/**
* No content types found. Please create a content type first.
*/
helpersQuestionsSelectContentTypeNoContentTypeWarning = 'helpers.questions.selectContentType.noContentType.warning',
/**
* Content type
*/
helpersQuestionsSelectContentTypeQuickPickTitle = 'helpers.questions.selectContentType.quickPick.title',
/**
* Select the content type to create your new content
*/
helpersQuestionsSelectContentTypeQuickPickPlaceholder = 'helpers.questions.selectContentType.quickPick.placeholder',
/**
* No content type was selected.
*/
helpersQuestionsSelectContentTypeNoSelectionWarning = 'helpers.questions.selectContentType.noSelection.warning',
/**
* Article {0} is longer than {1} characters (current length: {2}). For SEO reasons, it would be better to make it less than {1} characters.
*/
helpersSeoHelperCheckLengthDiagnosticMessage = 'helpers.seoHelper.checkLength.diagnostic.message',
/**
* You have local settings. Would you like to promote them to the global settings ("frontmatter.json")?
*/
helpersSettingsHelperCheckToPromoteMessage = 'helpers.settingsHelper.checkToPromote.message',
/**
* All settings promoted to team level.
*/
helpersSettingsHelperPromoteSuccess = 'helpers.settingsHelper.promote.success',
/**
* {0}: Reading dynamic config file...
*/
helpersSettingsHelperReadConfigProgressTitle = 'helpers.settingsHelper.readConfig.progress.title',
/**
* Error reading your configuration.
*/
helpersSettingsHelperReadConfigError = 'helpers.settingsHelper.readConfig.error',
/**
* Settings have been refreshed.
*/
helpersSettingsHelperRefreshConfigSuccess = 'helpers.settingsHelper.refreshConfig.success',
/**
* Rename the {0}
*/
helpersTaxonomyHelperRenameInputTitle = 'helpers.taxonomyHelper.rename.input.title',
/**
* The new value must be different from the old one.
*/
helpersTaxonomyHelperRenameValidateEqualValue = 'helpers.taxonomyHelper.rename.validate.equalValue',
/**
* A new value must be provided.
*/
helpersTaxonomyHelperRenameValidateNoValue = 'helpers.taxonomyHelper.rename.validate.noValue',
/**
* Merge the "{0}" with another {1} value
*/
helpersTaxonomyHelperMergeQuickPickTitle = 'helpers.taxonomyHelper.merge.quickPick.title',
/**
* Select the {0} value to merge with
*/
helpersTaxonomyHelperMergeQuickPickPlaceholder = 'helpers.taxonomyHelper.merge.quickPick.placeholder',
/**
* Delete the "{0}" {1} value
*/
helpersTaxonomyHelperDeleteQuickPickTitle = 'helpers.taxonomyHelper.delete.quickPick.title',
/**
* Are you sure you want to delete the "{0}" {1} value?
*/
helpersTaxonomyHelperDeleteQuickPickPlaceholder = 'helpers.taxonomyHelper.delete.quickPick.placeholder',
/**
* Create a new {0} value
*/
helpersTaxonomyHelperCreateNewInputTitle = 'helpers.taxonomyHelper.createNew.input.title',
/**
* Enter the value you want to add
*/
helpersTaxonomyHelperCreateNewInputPlaceholder = 'helpers.taxonomyHelper.createNew.input.placeholder',
/**
* A value must be provided.
*/
helpersTaxonomyHelperCreateNewInputValidateNoValue = 'helpers.taxonomyHelper.createNew.input.validate.noValue',
/**
* The value already exists.
*/
helpersTaxonomyHelperCreateNewInputValidateExists = 'helpers.taxonomyHelper.createNew.input.validate.exists',
/**
* {0}: Renaming "{1}" from {2} to {3}.
*/
helpersTaxonomyHelperProcessEdit = 'helpers.taxonomyHelper.process.edit',
/**
* {0}: Merging "{1}" from {2} to {3}.
*/
helpersTaxonomyHelperProcessMerge = 'helpers.taxonomyHelper.process.merge',
/**
* {0}: Deleting "{1}" from {2}.
*/
helpersTaxonomyHelperProcessDelete = 'helpers.taxonomyHelper.process.delete',
/**
* Edit completed.
*/
helpersTaxonomyHelperProcessEditSuccess = 'helpers.taxonomyHelper.process.edit.success',
/**
* Merge completed.
*/
helpersTaxonomyHelperProcessMergeSuccess = 'helpers.taxonomyHelper.process.merge.success',
/**
* Deletion completed.
*/
helpersTaxonomyHelperProcessDeleteSuccess = 'helpers.taxonomyHelper.process.delete.success',
/**
* Move the "{0}" to another type
*/
helpersTaxonomyHelperMoveQuickPickTitle = 'helpers.taxonomyHelper.move.quickPick.title',
/**
* Select the type to move to
*/
helpersTaxonomyHelperMoveQuickPickPlaceholder = 'helpers.taxonomyHelper.move.quickPick.placeholder',
/**
* {0}: Moving "{1}" from {2} to "${3}".
*/
helpersTaxonomyHelperMoveProgressTitle = 'helpers.taxonomyHelper.move.progress.title',
/**
* Move completed.
*/
helpersTaxonomyHelperMoveSuccess = 'helpers.taxonomyHelper.move.success',
/**
* Open the "frontmatter.json" file if you want to review the configuration.
*/
listenersDashboardDashboardListenerOpenConfigNotification = 'listeners.dashboard.dashboardListener.openConfig.notification',
/**
* No path provided.
*/
listenersDashboardDashboardListenerPinItemNoPathError = 'listeners.dashboard.dashboardListener.pinItem.noPath.error',
/**
* Could not pin item.
*/
listenersDashboardDashboardListenerPinItemCoundNotPinError = 'listeners.dashboard.dashboardListener.pinItem.coundNotPin.error',
/**
* Could not unpin item.
*/
listenersDashboardDashboardListenerPinItemCoundNotUnPinError = 'listeners.dashboard.dashboardListener.pinItem.coundNotUnPin.error',
/**
* Template files copied.
*/
listenersDashboardSettingsListenerTriggerTemplateNotification = 'listeners.dashboard.settingsListener.triggerTemplate.notification'
listenersDashboardSettingsListenerTriggerTemplateNotification = 'listeners.dashboard.settingsListener.triggerTemplate.notification',
/**
* Downloading and initializing the template...
*/
listenersDashboardSettingsListenerTriggerTemplateProgressTitle = 'listeners.dashboard.settingsListener.triggerTemplate.progress.title',
/**
* Failed to download the template.
*/
listenersDashboardSettingsListenerTriggerTemplateDownloadError = 'listeners.dashboard.settingsListener.triggerTemplate.download.error',
/**
* Failed to initialize the template.
*/
listenersDashboardSettingsListenerTriggerTemplateInitError = 'listeners.dashboard.settingsListener.triggerTemplate.init.error',
/**
* Snippet missing title or body
*/
listenersDashboardSnippetListenerAddSnippetMissingFieldsWarning = 'listeners.dashboard.snippetListener.addSnippet.missingFields.warning',
/**
* Snippet with the same title already exists
*/
listenersDashboardSnippetListenerAddSnippetExistsWarning = 'listeners.dashboard.snippetListener.addSnippet.exists.warning',
/**
* No snippets to update
*/
listenersDashboardSnippetListenerUpdateSnippetNoSnippetsWarning = 'listeners.dashboard.snippetListener.updateSnippet.noSnippets.warning',
/**
* Failed to push submodules.
*/
listenersGeneralGitListenerPushError = 'listeners.general.gitListener.push.error',
/**
* No active editor
*/
listenersPanelDataListenerAiSuggestTaxonomyNoEditorError = 'listeners.panel.dataListener.aiSuggestTaxonomy.noEditor.error',
/**
* No article data
*/
listenersPanelDataListenerAiSuggestTaxonomyNoDataError = 'listeners.panel.dataListener.aiSuggestTaxonomy.noData.error',
/**
* Couldn't find data file entries
*/
listenersPanelDataListenerGetDataFileEntriesNoDataFilesError = 'listeners.panel.dataListener.getDataFileEntries.noDataFiles.error',
/**
* No active editor
*/
listenersPanelTaxonomyListenerAiSuggestTaxonomyNoEditorError = 'listeners.panel.taxonomyListener.aiSuggestTaxonomy.noEditor.error',
/**
* No article data
*/
listenersPanelTaxonomyListenerAiSuggestTaxonomyNoDataError = 'listeners.panel.taxonomyListener.aiSuggestTaxonomy.noData.error',
/**
* Select the mode you want to use
*/
servicesModeSwitchSwitchModeQuickPickPlaceholder = 'services.modeSwitch.switchMode.quickPick.placeholder',
/**
* {0}: Mode selection
*/
servicesModeSwitchSwitchModeQuickPickTitle = 'services.modeSwitch.switchMode.quickPick.title',
/**
* Mode: {0}
*/
servicesModeSwitchSetTextMode = 'services.modeSwitch.setText.mode',
/**
* Processing...
*/
servicesPagesParserParsePagesStatusBarText = 'services.pagesParser.parsePages.statusBar.text',
/**
* File error: {0}
*/
servicesPagesParserParsePagesFileError = 'services.pagesParser.parsePages.file.error',
/**
* The AI title generation took too long. Please try again later.
*/
servicesSponsorAiGetTitlesWarning = 'services.sponsorAi.getTitles.warning',
/**
* The AI description generation took too long. Please try again later.
*/
servicesSponsorAiGetDescriptionWarning = 'services.sponsorAi.getDescription.warning',
/**
* The AI taxonomy generation took too long. Please try again later.
*/
servicesSponsorAiGetTaxonomySuggestionsWarning = 'services.sponsorAi.getTaxonomySuggestions.warning',
/**
* Starting local server
*/
servicesTerminalOpenLocalServerTerminalTerminalOptionMessage = 'services.terminal.openLocalServerTerminal.terminalOption.message'
}

View File

@@ -14,6 +14,7 @@ export interface AstroField {
| 'ZodEnum'
| 'ZodDate'
| 'ZodObject'
| 'datetime'
| 'email'
| 'url'
| 'image';

View File

@@ -65,6 +65,8 @@ export interface ContentType {
postScript?: string;
filePrefix?: string;
clearEmpty?: boolean;
isSubContent?: boolean;
allowAsSubContent?: boolean;
}
export type FieldType =

View File

@@ -114,7 +114,7 @@ const Actions: React.FunctionComponent<IActionsProps> = ({
{customActions?.length > 0 && (
<>
{actions?.length > 0 && <div className="divider py-4 w-full" style={{ height: `1px` }}></div>}
{actions?.length > 0 && <div className="divider w-full" style={{ height: `1px` }}></div>}
{...customActions}
</>

View File

@@ -2,7 +2,7 @@ import { Messenger } from '@estruyf/vscode/dist/client';
import * as React from 'react';
import { useCallback, useEffect, useState } from 'react';
import { DateHelper } from '../../../helpers/DateHelper';
import { BlockFieldData, CustomPanelViewResult, Field, PanelSettings, WhenOperator } from '../../../models';
import { BlockFieldData, CustomPanelViewResult, Field, PanelSettings } from '../../../models';
import { Command } from '../../Command';
import { CommandToCode } from '../../CommandToCode';
import { TagType } from '../../TagType';
@@ -36,6 +36,7 @@ import { LocalizationKey } from '../../../localization';
export interface IWrapperFieldProps {
field: Field;
allFields: Field[];
parent: IMetadata;
parentFields: string[];
metadata: IMetadata;
@@ -57,6 +58,7 @@ export interface IWrapperFieldProps {
export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
field,
allFields,
parent,
parentFields,
metadata,
@@ -158,7 +160,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
// Conditional fields
if (typeof field.when !== 'undefined') {
const shouldRender = fieldWhenClause(field, parent);
const shouldRender = fieldWhenClause(field, parent, allFields);
if (!shouldRender) {
return null;

View File

@@ -74,6 +74,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
<WrapperField
key={field.name}
field={field}
allFields={ctFields}
parent={parent}
parentFields={parentFields}
metadata={metadata}

View File

@@ -158,7 +158,8 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
value = item;
}
const uniqValues = Array.from(new Set([...selected, value]));
const safeSelected = selected instanceof Array ? selected : [];
const uniqValues = Array.from(new Set([...safeSelected, value]));
setSelected(uniqValues);
sendUpdate(uniqValues);
setInputValue('');
@@ -181,15 +182,19 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
* @param option
* @param inputValue
*/
const filterList = (option: string, inputValue: string | null) => {
const filterList = useCallback((option: string, inputValue: string | null) => {
if (typeof option !== 'string') {
return false;
}
if (!(selected instanceof Array)) {
return true;
}
return (
option && !selected.includes(option) && option.toLowerCase().includes((inputValue || '').toLowerCase())
);
};
}, [selected]);
/**
* Add the new item to the data
@@ -295,12 +300,18 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
}, [settings?.aiEnabled, label, type]);
const sortedSelectedTags = useMemo(() => {
return (selected || []).sort((a: string, b: string) => {
const aString = typeof a === 'string' ? a : `${a}`;
const bString = typeof b === 'string' ? b : `${b}`;
const safeSelected = selected || [];
return aString?.toLowerCase() < bString?.toLowerCase() ? -1 : 1;
});
if (safeSelected instanceof Array && safeSelected.length > 0) {
return (selected || []).sort((a: string, b: string) => {
const aString = typeof a === 'string' ? a : `${a}`;
const bString = typeof b === 'string' ? b : `${b}`;
return aString?.toLowerCase() < bString?.toLowerCase() ? -1 : 1;
});
}
return [];
}, [selected]);
useEffect(() => {

View File

@@ -1,6 +1,8 @@
import * as yaml from 'yaml';
import * as toml from '@iarna/toml';
import { Format, FrontMatterParser } from '.';
import { SETTING_QUOTE_STRINGS } from '../constants';
import { Settings } from '../helpers';
export const getFormatOpts = (format: string): Format => {
const formats: { [prop: string]: Format } = {
@@ -83,9 +85,12 @@ export const Engines = {
let updatedValue = docYaml.toJSON();
const quoteStrings = Settings.get(SETTING_QUOTE_STRINGS);
return yaml.stringify(updatedValue, {
lineWidth: options?.lineWidth || 5000,
defaultStringType: 'PLAIN',
defaultStringType: quoteStrings ? 'QUOTE_DOUBLE' : 'PLAIN',
defaultKeyType: 'PLAIN',
keepUndefined: false,
indent: options?.indent || 2,
indentSeq: options?.noArrayIndent ? false : true

View File

@@ -18,8 +18,7 @@ import {
} from 'vscode';
import {
SETTING_CONTENT_FRONTMATTER_HIGHLIGHT,
SETTING_CONTENT_SUPPORTED_FILETYPES,
SETTING_FRONTMATTER_TYPE
SETTING_CONTENT_SUPPORTED_FILETYPES
} from '../constants';
import { Settings } from '../helpers';
import { FrontMatterDecorationProvider } from './FrontMatterDecorationProvider';

View File

@@ -1,9 +1,15 @@
import { ModeListener } from './../listeners/general/ModeListener';
import { SETTING_GLOBAL_ACTIVE_MODE, SETTING_GLOBAL_MODES } from './../constants/settings';
import { commands, StatusBarAlignment, StatusBarItem, ThemeColor, window } from 'vscode';
import {
EXTENSION_NAME,
SETTING_GLOBAL_ACTIVE_MODE,
SETTING_GLOBAL_MODES
} from './../constants/settings';
import { commands, StatusBarAlignment, StatusBarItem, window } from 'vscode';
import { Settings } from '../helpers/SettingsHelper';
import { COMMAND_NAME, CONTEXT } from '../constants';
import { Mode } from '../models';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class ModeSwitch {
private static isInit: boolean = false;
@@ -56,9 +62,9 @@ export class ModeSwitch {
const modePicks = ['Default', ...modes.map((m) => m.id)];
const mode = await window.showQuickPick(modePicks, {
placeHolder: `Select the mode you want to use`,
ignoreFocusOut: true,
title: `Front Matter: Mode selection`
title: l10n.t(LocalizationKey.servicesModeSwitchSwitchModeQuickPickTitle, EXTENSION_NAME),
placeHolder: l10n.t(LocalizationKey.servicesModeSwitchSwitchModeQuickPickPlaceholder),
ignoreFocusOut: true
});
if (mode) {
@@ -70,9 +76,10 @@ export class ModeSwitch {
}
private static setText() {
ModeSwitch.statusBarElm.text = `$(preview) Mode: ${
ModeSwitch.statusBarElm.text = `$(preview) ${l10n.t(
LocalizationKey.servicesModeSwitchSetTextMode,
ModeSwitch.currentMode ? ModeSwitch.currentMode : 'Default'
}`;
)}`;
ModeSwitch.statusBarElm.show();
}
}

View File

@@ -26,6 +26,8 @@ import {
} from '../helpers';
import { existsAsync } from '../utils';
import { Article, Cache } from '../commands';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
export class PagesParser {
public static allPages: Page[] = [];
@@ -81,7 +83,9 @@ export class PagesParser {
const pages: Page[] = [];
if (folderInfo) {
PagesParser.pagesStatusBar.text = '$(sync~spin) Processing pages...';
PagesParser.pagesStatusBar.text = `$(sync~spin) ${l10n.t(
LocalizationKey.servicesPagesParserParsePagesStatusBarText
)}`;
PagesParser.pagesStatusBar.show();
for (const folder of folderInfo) {
@@ -108,7 +112,12 @@ export class PagesParser {
}
Logger.error(`PagesParser::parsePages: ${file.filePath} - ${error.message}`);
Notifications.error(`File error: ${file.filePath} - ${error?.message || error}`);
Notifications.error(
l10n.t(
LocalizationKey.servicesPagesParserParsePagesFileError,
`${file.filePath} - ${error?.message || error}`
)
);
}
}
}

View File

@@ -3,6 +3,8 @@ import { Logger, Notifications, Settings, TaxonomyHelper } from '../helpers';
import fetch from 'node-fetch';
import { TagType } from '../panelWebView/TagType';
import { TaxonomyType } from '../models';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
const AI_URL = 'https://frontmatter.codes/api/ai';
// const AI_URL = 'http://localhost:3000/api/ai';
@@ -18,7 +20,7 @@ export class SponsorAi {
try {
const controller = new AbortController();
const timeout = setTimeout(() => {
Notifications.warning(`The AI title generation took too long. Please try again later.`);
Notifications.warning(l10n.t(LocalizationKey.servicesSponsorAiGetTitlesWarning));
controller.abort();
}, 10000);
const signal = controller.signal;
@@ -51,7 +53,7 @@ export class SponsorAi {
try {
const controller = new AbortController();
const timeout = setTimeout(() => {
Notifications.warning(`The AI title generation took too long. Please try again later.`);
Notifications.warning(l10n.t(LocalizationKey.servicesSponsorAiGetDescriptionWarning));
controller.abort();
}, 10000);
const signal = controller.signal;
@@ -103,7 +105,9 @@ export class SponsorAi {
try {
const controller = new AbortController();
const timeout = setTimeout(() => {
Notifications.warning(`The AI taxonomy generation took too long. Please try again later.`);
Notifications.warning(
l10n.t(LocalizationKey.servicesSponsorAiGetTaxonomySuggestionsWarning)
);
controller.abort();
}, 10000);
const signal = controller.signal;

View File

@@ -1,6 +1,8 @@
import { workspace, window, ThemeIcon, TerminalOptions } from 'vscode';
import * as os from 'os';
import { Folders } from '../commands';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
interface ShellSetting {
path: string;
@@ -45,7 +47,9 @@ export class Terminal {
const terminalOptions: TerminalOptions = {
name: Terminal.terminalName,
iconPath: new ThemeIcon('server-environment'),
message: `Starting local server`
message: l10n.t(
LocalizationKey.servicesTerminalOpenLocalServerTerminalTerminalOptionMessage
)
};
// Check if workspace

View File

@@ -3,17 +3,25 @@ import { Field, WhenOperator } from '../models';
import { IMetadata } from '../panelWebView/components/Metadata';
/**
* Validate the field its "when" clause
* @param field
* @param parent
* @returns
* Determines whether a field should be displayed based on its "when" clause.
* @param field - The field to check.
* @param parent - The parent metadata object.
* @returns A boolean indicating whether the field should be displayed.
*/
export const fieldWhenClause = (field: Field, parent: IMetadata): boolean => {
export const fieldWhenClause = (field: Field, parent: IMetadata, allFields?: Field[]): boolean => {
const when = field.when;
if (!when) {
return true;
}
let parentField = allFields?.find((f) => f.name === when.fieldRef);
if (parentField && parentField.when) {
const renderParent = fieldWhenClause(parentField, parent, allFields);
if (!renderParent) {
return false;
}
}
let whenValue = parent[when.fieldRef];
if (when.caseSensitive || typeof when.caseSensitive === 'undefined') {
return caseSensitive(when, field, whenValue);
@@ -23,11 +31,11 @@ export const fieldWhenClause = (field: Field, parent: IMetadata): boolean => {
};
/**
* Case sensitive checks
* @param when
* @param field
* @param whenValue
* @returns
* Returns a boolean indicating whether the given `when` clause matches the given `field` and `whenValue`, ignoring case sensitivity.
* @param when - The `WhenClause` to match against.
* @param field - The `Field` to match against.
* @param whenValue - The value to match against the `when` clause.
* @returns A boolean indicating whether the `when` clause matches the `field` and `whenValue`, ignoring case sensitivity.
*/
const caseInsensitive = (
when: WhenClause,
@@ -43,11 +51,11 @@ const caseInsensitive = (
};
/**
* Case insensitive checks
* @param when
* @param field
* @param whenValue
* @returns
* Determines if a given field matches a when clause with case sensitivity.
* @param when - The when clause to match against.
* @param field - The field to match.
* @param whenValue - The value to match against the when clause.
* @returns True if the field matches the when clause, false otherwise.
*/
const caseSensitive = (
when: WhenClause,
@@ -119,9 +127,9 @@ const caseSensitive = (
};
/**
* Lower the value(s)
* @param value
* @returns
* Converts the given string or array of strings to lowercase.
* @param value - The string or array of strings to convert to lowercase.
* @returns The converted string or array of strings.
*/
const lowerValue = (value: string | string[] | any) => {
if (typeof value === 'string') {

View File

@@ -1,32 +1,35 @@
import { writeFileSync } from "fs";
import { join } from "path";
import { createServer } from "vite";
import zod from "zod";
import { writeFileSync } from 'fs';
import { join } from 'path';
import { createServer } from 'vite';
import zod from 'astro/zod';
const {
ZodDefault,
ZodObject,
ZodOptional,
ZodString,
ZodEffects,
ZodEnum
} = zod;
const { ZodDefault, ZodObject, ZodOptional, ZodString, ZodEffects, ZodEnum, ZodUnion } = zod;
/**
* Process the Zod field
* @param {ZodTypeAny} field
* @param {string} defaultValue
* @returns
* @param {ZodTypeAny} field
* @param {boolean} isOptional
* @param {string} defaultValue
* @returns
*/
function getField(field, defaultValue = undefined) {
let isOptional = false;
function getField(field, isOptional = false, defaultValue = undefined) {
// Handle various type transformations and assignments
if (field instanceof ZodOptional) {
isOptional = true;
const type = field.unwrap();
return getField(type, isOptional, defaultValue)
return getField(type, isOptional, defaultValue);
}
if (field instanceof ZodEffects) {
const type = field.sourceType();
return getField(type, isOptional, defaultValue);
}
if (field instanceof ZodUnion) {
const types = field._def.options;
const type = types[0];
return getField(type, isOptional, defaultValue);
}
if (field instanceof ZodDefault) {
@@ -35,43 +38,43 @@ function getField(field, defaultValue = undefined) {
defaultValue = field.parse(undefined);
// https://github.com/sachinraja/zod-to-ts/blob/main/src/index.ts
const type = field._def.innerType;
return getField(type, isOptional, defaultValue)
return getField(type, isOptional, defaultValue);
}
return {
type: field,
isOptional,
defaultValue
}
};
}
/**
* Generate the field information
* @param {string} name
* @param {ZodTypeAny} type
* @returns
* @param {string} name
* @param {ZodTypeAny} type
* @returns
*/
function generateFieldInfo(name, type) {
let description = type.description;
let defaultValue = undefined;
const {
type: fieldType,
isOptional: isFieldOptional,
defaultValue: fieldDefaultValue
} = getField(type, defaultValue);
const {
type: fieldType,
isOptional: isFieldOptional,
defaultValue: fieldDefaultValue
} = getField(type, false, defaultValue);
const fieldInfo = {
name: name,
description: description,
defaultValue: fieldDefaultValue,
type: fieldType._def.typeName,
required: !isFieldOptional,
required: !isFieldOptional
};
if (fieldType instanceof ZodObject) {
const subFields = extractFieldInfoFromShape(fieldType);
fieldInfo.fields = subFields;
}
@@ -79,6 +82,10 @@ function generateFieldInfo(name, type) {
fieldInfo.type = fieldType.sourceType().fmFieldType;
}
if (fieldInfo.name.toLowerCase().includes('image')) {
fieldInfo.type = 'image';
}
if (fieldType instanceof ZodEnum) {
fieldInfo.options = fieldType.options;
}
@@ -96,8 +103,8 @@ function generateFieldInfo(name, type) {
/**
* Parse the scheme into an array of fields
* @param {ZodTypeAny} type
* @returns
* @param {ZodTypeAny} type
* @returns
*/
function extractFieldInfoFromShape(type) {
if (type instanceof ZodOptional) {
@@ -106,7 +113,7 @@ function extractFieldInfoFromShape(type) {
if (!(type instanceof ZodObject)) {
// Return an empty array if the type is not of the expected type
return [];
return [];
}
// Iterate through the shape properties
@@ -121,8 +128,8 @@ function extractFieldInfoFromShape(type) {
/**
* Process each content collection
* @param {*} collections
* @returns
* @param {*} collections
* @returns
*/
function processCollection(collections) {
if (!Array.isArray(collections)) {
@@ -130,32 +137,35 @@ function processCollection(collections) {
}
return collections.map(([name, collection]) => {
const schema = typeof collection.schema === "function" ? collection.schema({
image() {
const field = zod.string();
field.fmFieldType = "image";
return field;
}
}) : collection.schema;
const schema =
typeof collection.schema === 'function'
? collection.schema({
image() {
const field = zod.string();
field.fmFieldType = 'image';
return field;
}
})
: collection.schema;
return {
name,
type: collection.type || "content",
fields: extractFieldInfoFromShape(schema),
type: collection.type || 'content',
fields: extractFieldInfoFromShape(schema)
};
});
}
/**
* More info: https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention
* @returns
* @returns
*/
const astroContentModulePlugin = () => {
const astroContent = "astro:content";
const astroContent = 'astro:content';
const astroContentMarker = `\0${astroContent}`;
return {
name: "astro-content-collections",
name: 'astro-content-collections',
resolveId(importee) {
if (importee === astroContent) {
return astroContentMarker;
@@ -174,7 +184,7 @@ const astroContentModulePlugin = () => {
`;
}
}
}
};
};
/**
@@ -182,8 +192,8 @@ const astroContentModulePlugin = () => {
*/
(async () => {
const configPath = process.argv[2];
if (!configPath || typeof configPath !== "string") {
console.log("No config path provided");
if (!configPath || typeof configPath !== 'string') {
console.log('No config path provided');
process.exit(1);
}
@@ -195,22 +205,25 @@ const astroContentModulePlugin = () => {
hmr: false
},
optimizeDeps: {
disabled: true,
disabled: true
},
appType: "custom",
appType: 'custom',
plugins: [astroContentModulePlugin()]
});
try {
// https://github.dev/withastro/astro/blob/defab70cb2a0c67d5e9153542490d2749046b151/packages/astro/src/content/utils.ts#L334
const contentModule = await server.ssrLoadModule(configPath)
const contentModule = await server.ssrLoadModule(configPath);
const collections = Object.entries(contentModule.collections ?? {});
const processedCollections = processCollection(collections);
writeFileSync(join(process.cwd(), `./.frontmatter/temp/astro.collections.json`), JSON.stringify(processedCollections));
console.log("Collections generated successfully");
writeFileSync(
join(process.cwd(), `./.frontmatter/temp/astro.collections.json`),
JSON.stringify(processedCollections, null, 2)
);
console.log('Collections generated successfully');
process.exit(0);
} catch (error) {
console.log(error.message);
@@ -218,4 +231,4 @@ const astroContentModulePlugin = () => {
} finally {
await server.close();
}
})();
})();