Compare commits

...

11 Commits

13 changed files with 136 additions and 32 deletions
+5 -2
View File
@@ -6,7 +6,7 @@ src/**
vsc-extension-quickstart.md
**/tsconfig.json
**/tsconfig.e2e.json
**/*.map
**/*.ts
webpack.config.js
node_modules
@@ -29,4 +29,7 @@ webpack
README.beta.md
e2e
storage
pnpm-lock.yaml
pnpm-lock.yaml
.env
tailwind.panel.js
LOCALIZATION.md
+8
View File
@@ -1,5 +1,13 @@
# Change Log
## [10.10.1] - 2026-04-23
### 🐞 Fixes
- [#1023](https://github.com/estruyf/vscode-front-matter/issues/1023): Fix validation errors for image, file, and keywords fields
- [#1024](https://github.com/estruyf/vscode-front-matter/issues/1024): Re-add the `frontMatter.copilot.enabled` setting to allow users to disable the GitHub Copilot integration
- Fix Git detection when Git is configured via VS Code `git.path` and not installed globally on the system
## [10.10.0] - 2026-04-03 - [Release notes](https://beta.frontmatter.codes/updates/v10.10.0)
- Removed the chatbot command and all related code and references
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "vscode-front-matter-beta",
"version": "10.10.0",
"version": "10.10.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-front-matter-beta",
"version": "10.10.0",
"version": "10.10.1",
"license": "MIT",
"devDependencies": {
"@actions/core": "^1.10.0",
+6 -1
View File
@@ -3,7 +3,7 @@
"displayName": "Front Matter CMS",
"description": "Front Matter is a CMS that runs within Visual Studio Code. It gives you the power and control of a full-blown CMS while also providing you the flexibility and speed of the static site generator of your choice like: Hugo, Jekyll, Docusaurus, NextJs, Gatsby, and many more...",
"icon": "assets/frontmatter-teal-128x128.png",
"version": "10.10.0",
"version": "10.10.1",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
@@ -2128,6 +2128,11 @@
"type": "string",
"default": "gpt-4o-mini",
"markdownDescription": "%setting.frontMatter.copilot.family.markdownDescription%"
},
"frontMatter.copilot.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "%setting.frontMatter.copilot.enabled.markdownDescription%"
}
}
},
+2 -1
View File
@@ -292,5 +292,6 @@
"setting.frontMatter.git.disableOnBranches.markdownDescription": "Specify the branches on which you want to disable the Git actions. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.disableonbranches%22%5D)",
"setting.frontMatter.git.requiresCommitMessage.markdownDescription": "Specify if you want to require a commit message when publishing your changes for a specified branch. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.git.requirescommitmessage%22%5D)",
"setting.frontMatter.copilot.family.markdownDescription": "Specify the LLM family of the Copilot you want to use. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.copilot.family) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.copilot.family%22%5D)"
"setting.frontMatter.copilot.family.markdownDescription": "Specify the LLM family of the Copilot you want to use. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.copilot.family) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.copilot.family%22%5D)",
"setting.frontMatter.copilot.enabled.markdownDescription": "Specify if you want to enable GitHub Copilot AI suggestions (requires GitHub Copilot extension). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.copilot.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.copilot.enabled%22%5D)"
}
+1
View File
@@ -117,6 +117,7 @@ export const SETTING_SNIPPETS_WRAPPER = 'snippets.wrapper.enabled';
export const SETTING_WEBSITE_URL = 'website.host';
export const SETTING_COPILOT_FAMILY = 'copilot.family';
export const SETTING_COPILOT_ENABLED = 'copilot.enabled';
export const SETTING_LOGGING = 'logging';
+23 -11
View File
@@ -26,11 +26,11 @@ export interface JSONSchema {
/**
* Generates JSON Schema from Front Matter Content Type definitions
*
*
* This utility converts Front Matter content type definitions into JSON Schema format
* which can then be used for validation. It handles all field types supported by
* Front Matter CMS including nested fields, blocks, and field groups.
*
*
* Field Type Mappings:
* - string, slug, image, file, customField → string
* - number → number (with optional min/max)
@@ -42,7 +42,7 @@ export interface JSONSchema {
* - block → array of objects with oneOf for field groups
* - json → any valid JSON type
* - dataFile, contentRelationship → string or array
*
*
* Features:
* - Required field validation
* - Type validation
@@ -50,7 +50,7 @@ export interface JSONSchema {
* - Number range validation (min/max)
* - Nested object support
* - Block field support with multiple field group options
*
*
* Usage:
* ```typescript
* const schema = ContentTypeSchemaGenerator.generateSchema(contentType);
@@ -123,12 +123,20 @@ export class ContentTypeSchemaGenerator {
switch (field.type) {
case 'string':
case 'slug':
case 'image':
case 'file':
case 'customField':
schema.type = 'string';
break;
case 'image':
case 'file':
if (field.multiple) {
schema.type = 'array';
schema.items = { type: 'string' };
} else {
schema.type = 'string';
}
break;
case 'number':
schema.type = 'number';
if (field.numberOptions) {
@@ -173,7 +181,7 @@ export class ContentTypeSchemaGenerator {
schema.items = {
type: 'string'
};
// Get available tags and add as enum for validation
const availableTags = await TaxonomyHelper.get(TaxonomyType.Tag);
if (availableTags && availableTags.length > 0) {
@@ -187,7 +195,7 @@ export class ContentTypeSchemaGenerator {
schema.items = {
type: 'string'
};
// Get available categories and add as enum for validation
const availableCategories = await TaxonomyHelper.get(TaxonomyType.Category);
if (availableCategories && availableCategories.length > 0) {
@@ -201,7 +209,7 @@ export class ContentTypeSchemaGenerator {
schema.items = {
type: 'string'
};
// Get custom taxonomy options if taxonomyId is specified
if (field.taxonomyId) {
const customTaxonomies = Settings.get<CustomTaxonomy[]>(SETTING_TAXONOMY_CUSTOM);
@@ -307,7 +315,9 @@ export class ContentTypeSchemaGenerator {
* @param choices Array of choice strings or objects
* @returns Array of choice values
*/
private static extractChoiceValues(choices: (string | { id?: string | null; title: string })[]): string[] {
private static extractChoiceValues(
choices: (string | { id?: string | null; title: string })[]
): string[] {
return choices.map((choice) => {
if (typeof choice === 'string') {
return choice;
@@ -330,7 +340,9 @@ export class ContentTypeSchemaGenerator {
}
const fieldGroupIds = Array.isArray(field.fieldGroup) ? field.fieldGroup : [field.fieldGroup];
const fieldGroups = Settings.get(SETTING_TAXONOMY_FIELD_GROUPS) as { id: string; fields: Field[] }[] | undefined;
const fieldGroups = Settings.get(SETTING_TAXONOMY_FIELD_GROUPS) as
| { id: string; fields: Field[] }[]
| undefined;
if (!fieldGroups || fieldGroups.length === 0) {
return schemas;
+3 -1
View File
@@ -1,7 +1,8 @@
import {
SETTING_GLOBAL_TIMEZONE,
SETTING_PANEL_ACTIONS_DISABLED,
SETTING_WEBSITE_URL
SETTING_WEBSITE_URL,
SETTING_COPILOT_ENABLED
} from './../constants/settings';
import { workspace } from 'vscode';
import { ContentType, Extension, Logger, Settings, TaxonomyHelper } from '.';
@@ -51,6 +52,7 @@ export class PanelSettings {
try {
return {
aiEnabled: Settings.get<boolean>(SETTING_COPILOT_ENABLED) !== false,
copilotEnabled: await Copilot.isInstalled(),
git: await GitListener.getSettings(),
seo: {
+5 -1
View File
@@ -1,8 +1,10 @@
import { QuickPickItem, QuickPickItemKind, window } from 'vscode';
import { Folders } from '../commands/Folders';
import { SETTING_COPILOT_ENABLED } from '../constants';
import { ContentType } from './ContentType';
import { Notifications } from './Notifications';
import { Logger } from './Logger';
import { Settings } from './SettingsHelper';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { ContentFolder } from '../models';
@@ -37,12 +39,14 @@ export class Questions {
* @returns
*/
public static async ContentTitle(showWarning = true): Promise<string | undefined> {
const copilotEnabled = Settings.get<boolean>(SETTING_COPILOT_ENABLED) !== false;
let title: string | undefined = '';
const isCopilotInstalled = await Copilot.isInstalled();
let aiTitles: string[] | undefined;
if (isCopilotInstalled) {
// Only show AI suggestions if both the setting is enabled and Copilot is installed
if (copilotEnabled && isCopilotInstalled) {
title = await window.showInputBox({
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
+38 -12
View File
@@ -26,7 +26,7 @@ import {
import { GeneralCommands } from './../../constants/GeneralCommands';
import simpleGit, { SimpleGit } from 'simple-git';
import { Folders } from '../../commands/Folders';
import { Event, commands, extensions } from 'vscode';
import { Event, commands, extensions, workspace } from 'vscode';
import { GitAPIState, GitRepository, PostMessageData } from '../../models';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
@@ -69,16 +69,21 @@ export class GitListener {
const gitActions = Settings.get<boolean>(SETTING_GIT_ENABLED);
if (gitActions) {
Logger.verbose('GitListener:getSettings:end:enabled');
return {
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
actions: gitActions || false,
disabledBranches: gitActions
? Settings.get<string[]>(SETTING_GIT_DISABLED_BRANCHES) || []
: [],
requiresCommitMessage: gitActions
? Settings.get<string[]>(SETTING_GIT_REQUIRES_COMMIT_MSG) || []
: []
};
try {
return {
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
actions: gitActions || false,
disabledBranches: gitActions
? Settings.get<string[]>(SETTING_GIT_DISABLED_BRANCHES) || []
: [],
requiresCommitMessage: gitActions
? Settings.get<string[]>(SETTING_GIT_REQUIRES_COMMIT_MSG) || []
: []
};
} catch (e) {
Logger.error((e as Error).message);
return;
}
}
Logger.verbose('GitListener:getSettings:end:disabled');
@@ -338,7 +343,7 @@ export class GitListener {
const options = {
baseDir: submoduleFolder || wsFolder?.fsPath || '',
binary: 'git',
binary: GitListener.getGitBinary(),
maxConcurrentProcesses: 6
};
@@ -351,6 +356,27 @@ export class GitListener {
}
}
/**
* Resolves the Git binary path from VS Code settings.
* Falls back to the default `git` command when no custom path is configured.
*/
private static getGitBinary(): string {
const gitPath = workspace.getConfiguration('git').get<string | string[]>('path');
if (Array.isArray(gitPath)) {
const firstValidPath = gitPath.find((path) => typeof path === 'string' && path.trim());
if (firstValidPath) {
return firstValidPath;
}
}
if (typeof gitPath === 'string' && gitPath.trim()) {
return gitPath;
}
return 'git';
}
/**
* Initializes the VS Code Git provider and sets up event listeners for repository changes.
* @returns {Promise<void>} A promise that resolves when the Git provider is initialized.
+26 -1
View File
@@ -26,7 +26,8 @@ import {
SETTING_DATE_FORMAT,
SETTING_GLOBAL_ACTIVE_MODE,
SETTING_GLOBAL_MODES,
SETTING_TAXONOMY_CONTENT_TYPES
SETTING_TAXONOMY_CONTENT_TYPES,
SETTING_COPILOT_ENABLED
} from '../../constants';
import { Article, Preview } from '../../commands';
import { FrontMatterParser, ParsedFrontMatter } from '../../parsers';
@@ -124,6 +125,15 @@ export class DataListener extends BaseListener {
return;
}
// Check if Copilot is enabled and installed
const copilotEnabled = Settings.get<boolean>(SETTING_COPILOT_ENABLED) !== false;
const isCopilotInstalled = await Copilot.isInstalled();
if (!copilotEnabled || !isCopilotInstalled) {
this.sendRequestError(command, requestId, 'Copilot is not enabled or installed');
return;
}
const aiTitles = await Copilot.suggestTitles(title);
title = await Questions.pickTitleSuggestions(title, aiTitles || [], true);
@@ -145,6 +155,21 @@ export class DataListener extends BaseListener {
return;
}
// Check if Copilot is enabled and installed
const copilotEnabled = Settings.get<boolean>(SETTING_COPILOT_ENABLED) !== false;
const isCopilotInstalled = await Copilot.isInstalled();
if (!copilotEnabled || !isCopilotInstalled) {
const extPath = Extension.getInstance().extensionPath;
const panel = PanelProvider.getInstance(extPath);
panel.getWebview()?.postMessage({
command,
requestId,
error: l10n.t(LocalizationKey.servicesCopilotGetChatResponseError)
} as MessageHandlerData<string>);
return;
}
const article = ArticleHelper.getActiveFile();
if (!article) {
return;
+16
View File
@@ -4,6 +4,7 @@ import { BaseListener } from './BaseListener';
import { authentication, window } from 'vscode';
import { ArticleHelper, Extension, Settings, TaxonomyHelper } from '../../helpers';
import { BlockFieldData, CustomTaxonomyData, PostMessageData, TaxonomyType } from '../../models';
import { SETTING_COPILOT_ENABLED } from '../../constants';
import { DataListener } from '.';
import { SettingsListener as PanelSettingsListener } from '.';
import { SettingsListener as DashboardSettingsListener } from '../dashboard';
@@ -84,6 +85,21 @@ export class TaxonomyListener extends BaseListener {
return;
}
// Check if Copilot is enabled and installed
const copilotEnabled = Settings.get<boolean>(SETTING_COPILOT_ENABLED) !== false;
const isCopilotInstalled = await Copilot.isInstalled();
if (!copilotEnabled || !isCopilotInstalled) {
const extPath = Extension.getInstance().extensionPath;
const panel = PanelProvider.getInstance(extPath);
panel.getWebview()?.postMessage({
command,
requestId,
error: l10n.t(LocalizationKey.servicesCopilotGetChatResponseError)
} as MessageHandlerData<string[]>);
return;
}
const article = ArticleHelper.getActiveFile();
if (!article) {
return;
+1
View File
@@ -28,6 +28,7 @@ export interface PanelSettings {
dataTypes: DataType[] | undefined;
fieldGroups: FieldGroup[] | undefined;
commaSeparatedFields: string[];
aiEnabled: boolean;
copilotEnabled: boolean;
contentFolders: ContentFolder[];
websiteUrl: string;