mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
Compare commits
2 Commits
copilot/fi
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
beef6f36d8 | ||
|
|
d11dbc9d76 |
@@ -504,6 +504,7 @@
|
||||
"panel.seoKeywords.density": "Keyword density",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Heading(s)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Content",
|
||||
"panel.seoKeywordInfo.validInfo.firstParagraph": "First paragraph",
|
||||
"panel.seoKeywordInfo.density.tooltip": "Recommended frequency: 0.75% - 1.5%",
|
||||
|
||||
"panel.seoKeywords.title": "Keywords",
|
||||
|
||||
@@ -139,10 +139,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"frontMatter.ai.enabled": {
|
||||
"frontMatter.sponsors.ai.enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%setting.frontMatter.ai.enabled.markdownDescription%"
|
||||
"default": false,
|
||||
"markdownDescription": "%setting.frontMatter.sponsors.ai.enabled.markdownDescription%",
|
||||
"scope": "Sponsors"
|
||||
},
|
||||
"frontMatter.extensibility.scripts": {
|
||||
"type": "array",
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"setting.frontMatter.projects.markdownDescription": "Specify the list of projects to load in the Front Matter CMS. [Local](https://file%2B.vscode-resource.vscode-cdn.net/Users/eliostruyf/nodejs/frontmatter-test-projects/astro-blog/test.html) - [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.projects) - [View in VS Code](vscode://simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.projects%22%5D)",
|
||||
"setting.frontMatter.projects.items.properties.name.markdownDescription": "Specify the name of the project.",
|
||||
"setting.frontMatter.projects.items.properties.default.markdownDescription": "Specify if this project is the default project to load.",
|
||||
"setting.frontMatter.ai.enabled.markdownDescription": "Specify if you want to enable AI suggestions (requires GitHub Copilot extension). [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.ai.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.ai.enabled%22%5D)",
|
||||
"setting.frontMatter.sponsors.ai.enabled.markdownDescription": "Specify if you want to enable AI suggestions. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.sponsors.ai.enabled) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.sponsors.ai.enabled%22%5D)",
|
||||
"setting.frontMatter.extensibility.scripts.markdownDescription": "Specify the list of scripts to load in the Front Matter CMS. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.extensibility.scripts) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.extensibility.scripts%22%5D)",
|
||||
"setting.frontMatter.experimental.markdownDescription": "Specify if you want to enable the experimental features. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.experimental) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.experimental%22%5D)",
|
||||
"setting.frontMatter.extends.markdownDescription": "Specify the list of paths/URLs to extend the Front Matter CMS config. [Docs](https://frontmatter.codes/docs/settings/overview#frontmatter.extends) - [View in VS Code](command:simpleBrowser.show?%5B%22https://frontmatter.codes/docs/settings/overview%23frontmatter.extends%22%5D)",
|
||||
|
||||
@@ -117,10 +117,14 @@ 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_AI_ENABLED = 'ai.enabled';
|
||||
|
||||
export const SETTING_LOGGING = 'logging';
|
||||
|
||||
/**
|
||||
* Sponsors only settings
|
||||
*/
|
||||
export const SETTING_SPONSORS_AI_ENABLED = 'sponsors.ai.enabled';
|
||||
|
||||
/**
|
||||
* Project override support
|
||||
*/
|
||||
|
||||
@@ -807,7 +807,8 @@ export class ArticleHelper {
|
||||
const elms: Parent[] | Link[] = this.getAllElms(mdTree);
|
||||
|
||||
const headings = elms.filter((node) => node.type === 'heading');
|
||||
const paragraphs = elms.filter((node) => node.type === 'paragraph').length;
|
||||
const paragraphNodes = elms.filter((node) => node.type === 'paragraph');
|
||||
const paragraphs = paragraphNodes.length;
|
||||
const images = elms.filter((node) => node.type === 'image').length;
|
||||
const links: string[] = elms
|
||||
.filter((node) => node.type === 'link')
|
||||
@@ -836,6 +837,21 @@ export class ArticleHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract first paragraph text for SEO keyword checking
|
||||
let firstParagraph = '';
|
||||
if (paragraphNodes.length > 0) {
|
||||
const firstParagraphNode = paragraphNodes[0];
|
||||
const extractTextFromNode = (node: any): string => {
|
||||
if (node.type === 'text') {
|
||||
return node.value || '';
|
||||
} else if (node.children && Array.isArray(node.children)) {
|
||||
return node.children.map(extractTextFromNode).join('');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
firstParagraph = extractTextFromNode(firstParagraphNode);
|
||||
}
|
||||
|
||||
const wordCount = this.wordCount(0, mdTree);
|
||||
|
||||
return {
|
||||
@@ -846,7 +862,8 @@ export class ArticleHelper {
|
||||
internalLinks,
|
||||
externalLinks: externalLinks.length,
|
||||
wordCount,
|
||||
content: article.content
|
||||
content: article.content,
|
||||
firstParagraph
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
SETTING_GLOBAL_TIMEZONE,
|
||||
SETTING_PANEL_ACTIONS_DISABLED,
|
||||
SETTING_AI_ENABLED,
|
||||
SETTING_SPONSORS_AI_ENABLED,
|
||||
SETTING_WEBSITE_URL
|
||||
} from './../constants/settings';
|
||||
import { workspace } from 'vscode';
|
||||
@@ -52,7 +52,7 @@ export class PanelSettings {
|
||||
|
||||
try {
|
||||
return {
|
||||
aiEnabled: Settings.get<boolean>(SETTING_AI_ENABLED) !== false,
|
||||
aiEnabled: Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED) || false,
|
||||
copilotEnabled: await Copilot.isInstalled(),
|
||||
git: await GitListener.getSettings(),
|
||||
seo: {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { authentication, QuickPickItem, QuickPickItemKind, window } from 'vscode';
|
||||
import { Folders } from '../commands/Folders';
|
||||
import { SETTING_AI_ENABLED } from '../constants';
|
||||
import { SETTING_SPONSORS_AI_ENABLED } from '../constants';
|
||||
import { ContentType } from './ContentType';
|
||||
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';
|
||||
import { ContentFolder } from '../models';
|
||||
@@ -39,30 +40,56 @@ export class Questions {
|
||||
* @returns
|
||||
*/
|
||||
public static async ContentTitle(showWarning = true): Promise<string | undefined> {
|
||||
const aiEnabled = Settings.get<boolean>(SETTING_AI_ENABLED);
|
||||
const aiEnabled = Settings.get<boolean>(SETTING_SPONSORS_AI_ENABLED);
|
||||
let title: string | undefined = '';
|
||||
const isCopilotInstalled = await Copilot.isInstalled();
|
||||
|
||||
let aiTitles: string[] | undefined;
|
||||
|
||||
// Only show AI suggestions if both the setting is enabled and Copilot is installed
|
||||
if (aiEnabled !== false && isCopilotInstalled) {
|
||||
title = await window.showInputBox({
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
|
||||
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (aiEnabled || isCopilotInstalled) {
|
||||
if (isCopilotInstalled) {
|
||||
title = await window.showInputBox({
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
|
||||
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (title) {
|
||||
try {
|
||||
aiTitles = await Copilot.suggestTitles(title);
|
||||
} catch (e) {
|
||||
Logger.error((e as Error).message);
|
||||
Notifications.error(
|
||||
l10n.t(LocalizationKey.helpersQuestionsContentTitleCopilotInputFailed)
|
||||
);
|
||||
title = undefined;
|
||||
if (title) {
|
||||
try {
|
||||
aiTitles = await Copilot.suggestTitles(title);
|
||||
} catch (e) {
|
||||
Logger.error((e as Error).message);
|
||||
Notifications.error(
|
||||
l10n.t(LocalizationKey.helpersQuestionsContentTitleCopilotInputFailed)
|
||||
);
|
||||
title = undefined;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const githubAuth = await authentication.getSession('github', ['read:user'], {
|
||||
silent: true
|
||||
});
|
||||
|
||||
if (githubAuth && githubAuth.account.label) {
|
||||
title = await window.showInputBox({
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputTitle),
|
||||
prompt: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPrompt),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (title) {
|
||||
try {
|
||||
aiTitles = await SponsorAi.getTitles(githubAuth.accessToken, title);
|
||||
} catch (e) {
|
||||
Logger.error((e as Error).message);
|
||||
Notifications.error(
|
||||
l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputFailed)
|
||||
);
|
||||
title = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Folders } from '../../commands/Folders';
|
||||
import { Command } from '../../panelWebView/Command';
|
||||
import { CommandToCode } from '../../panelWebView/CommandToCode';
|
||||
import { BaseListener } from './BaseListener';
|
||||
import { Uri, commands, window } from 'vscode';
|
||||
import { Uri, authentication, commands, window } from 'vscode';
|
||||
import {
|
||||
ArticleHelper,
|
||||
Extension,
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
import { encodeEmoji, fieldWhenClause, getTitleField } 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';
|
||||
@@ -101,6 +102,9 @@ export class DataListener extends BaseListener {
|
||||
case CommandToCode.getDataEntries:
|
||||
this.getDataFileEntries(msg.command, msg.requestId || '', msg.payload);
|
||||
break;
|
||||
case CommandToCode.aiSuggestDescription:
|
||||
this.aiSuggestTaxonomy(msg.command, msg.requestId);
|
||||
break;
|
||||
case CommandToCode.copilotSuggestDescription:
|
||||
this.copilotSuggestDescription(msg.command, msg.requestId);
|
||||
break;
|
||||
@@ -175,6 +179,68 @@ export class DataListener extends BaseListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests taxonomy using AI.
|
||||
* @param command - The command string.
|
||||
* @param requestId - The optional request ID.
|
||||
*/
|
||||
private static async aiSuggestTaxonomy(command: string, requestId?: string) {
|
||||
if (!command || !requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = PanelProvider.getInstance(extPath);
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.listenersPanelDataListenerAiSuggestTaxonomyNoEditorError)
|
||||
} as MessageHandlerData<string>);
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (!article || !article.data) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.listenersPanelDataListenerAiSuggestTaxonomyNoDataError)
|
||||
} as MessageHandlerData<string>);
|
||||
return;
|
||||
}
|
||||
|
||||
const githubAuth = await authentication.getSession('github', ['read:user'], { silent: true });
|
||||
if (!githubAuth || !githubAuth.accessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const titleField = getTitleField();
|
||||
|
||||
const suggestion = await SponsorAi.getDescription(
|
||||
githubAuth.accessToken,
|
||||
article.data[titleField] || '',
|
||||
article.content || ''
|
||||
);
|
||||
|
||||
if (!suggestion) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.listenersPanelDataListenerAiSuggestTaxonomyNoDataError)
|
||||
} as MessageHandlerData<string>);
|
||||
return;
|
||||
}
|
||||
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
payload: suggestion || []
|
||||
} as MessageHandlerData<string>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the information about the registered folders and its files
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { CommandToCode } from '../../panelWebView/CommandToCode';
|
||||
import { TagType } from '../../panelWebView/TagType';
|
||||
import { BaseListener } from './BaseListener';
|
||||
import { window } from 'vscode';
|
||||
import { authentication, window } from 'vscode';
|
||||
import { ArticleHelper, Extension, Settings, TaxonomyHelper } from '../../helpers';
|
||||
import { BlockFieldData, CustomTaxonomyData, PostMessageData, TaxonomyType } from '../../models';
|
||||
import { DataListener } from '.';
|
||||
import { SponsorAi } from '../../services/SponsorAI';
|
||||
import { PanelProvider } from '../../panelWebView/PanelProvider';
|
||||
import { MessageHandlerData } from '@estruyf/vscode';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
@@ -59,6 +60,9 @@ export class TaxonomyListener extends BaseListener {
|
||||
case CommandToCode.addToCustomTaxonomy:
|
||||
this.addCustomTaxonomy(msg.payload);
|
||||
break;
|
||||
case CommandToCode.aiSuggestTaxonomy:
|
||||
this.aiSuggestTaxonomy(msg.command, msg.requestId, msg.payload);
|
||||
break;
|
||||
case CommandToCode.copilotSuggestTaxonomy:
|
||||
this.copilotSuggestTaxonomy(msg.command, msg.requestId, msg.payload);
|
||||
break;
|
||||
@@ -116,6 +120,73 @@ export class TaxonomyListener extends BaseListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests taxonomy based on the provided command, request ID, and tag type.
|
||||
*
|
||||
* @param command - The command to execute.
|
||||
* @param requestId - The ID of the request.
|
||||
* @param type - The type of tag.
|
||||
* @returns A Promise that resolves to void.
|
||||
*/
|
||||
private static async aiSuggestTaxonomy(command: string, requestId?: string, type?: TagType) {
|
||||
if (!command || !requestId || !type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = PanelProvider.getInstance(extPath);
|
||||
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.listenersPanelTaxonomyListenerAiSuggestTaxonomyNoDataError)
|
||||
} as MessageHandlerData<string>);
|
||||
return;
|
||||
}
|
||||
|
||||
const article = ArticleHelper.getFrontMatter(editor);
|
||||
if (!article || !article.data) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.listenersPanelTaxonomyListenerAiSuggestTaxonomyNoEditorError)
|
||||
} as MessageHandlerData<string>);
|
||||
return;
|
||||
}
|
||||
|
||||
const githubAuth = await authentication.getSession('github', ['read:user'], { silent: true });
|
||||
if (!githubAuth || !githubAuth.accessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const titleField = getTitleField();
|
||||
const descriptionField = getDescriptionField();
|
||||
|
||||
const suggestions = await SponsorAi.getTaxonomySuggestions(
|
||||
githubAuth.accessToken,
|
||||
article.data[titleField] || '',
|
||||
article.data[descriptionField] || '',
|
||||
type
|
||||
);
|
||||
|
||||
if (!suggestions) {
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
error: l10n.t(LocalizationKey.listenersPanelTaxonomyListenerAiSuggestTaxonomyNoDataError)
|
||||
} as MessageHandlerData<string>);
|
||||
return;
|
||||
}
|
||||
|
||||
panel.getWebview()?.postMessage({
|
||||
command,
|
||||
requestId,
|
||||
payload: suggestions || []
|
||||
} as MessageHandlerData<string[]>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tags in the current document
|
||||
* @param tagType
|
||||
|
||||
@@ -1632,6 +1632,10 @@ export enum LocalizationKey {
|
||||
* Content
|
||||
*/
|
||||
panelSeoKeywordInfoValidInfoContent = 'panel.seoKeywordInfo.validInfo.content',
|
||||
/**
|
||||
* First paragraph
|
||||
*/
|
||||
panelSeoKeywordInfoValidInfoFirstParagraph = 'panel.seoKeywordInfo.validInfo.firstParagraph',
|
||||
/**
|
||||
* Recommended frequency: 0.75% - 1.5%
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,8 @@ export enum CommandToCode {
|
||||
getDataEntries = 'get-data-entries',
|
||||
generateSlug = 'generate-slug',
|
||||
stopServer = 'stop-server',
|
||||
aiSuggestTaxonomy = 'ai-suggest-taxonomy',
|
||||
aiSuggestDescription = 'ai-suggest-description',
|
||||
copilotSuggestTitle = 'copilot-suggest-title',
|
||||
copilotSuggestDescription = 'copilot-suggest-description',
|
||||
copilotSuggestTaxonomy = 'copilot-suggest-taxonomy',
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface IArticleDetailsProps {
|
||||
internalLinks: number;
|
||||
externalLinks: number;
|
||||
images: number;
|
||||
firstParagraph?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -260,11 +260,13 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
}
|
||||
|
||||
const suggestTaxonomy = useCallback(
|
||||
(type: TagType) => {
|
||||
(aiType: 'ai' | 'copilot', type: TagType) => {
|
||||
setLoading(localize(LocalizationKey.panelTagPickerAiGenerating));
|
||||
|
||||
const command =
|
||||
aiType === 'ai' ? CommandToCode.aiSuggestTaxonomy : CommandToCode.copilotSuggestTaxonomy;
|
||||
messageHandler
|
||||
.request<string[]>(CommandToCode.copilotSuggestTaxonomy, type)
|
||||
.request<string[]>(command, type)
|
||||
.then((values) => {
|
||||
setLoading(undefined);
|
||||
updateTaxonomy(values)
|
||||
@@ -309,7 +311,22 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{settings?.aiEnabled && settings?.copilotEnabled && (
|
||||
{settings?.aiEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action"
|
||||
title={localize(
|
||||
LocalizationKey.panelTagPickerAiSuggest,
|
||||
label?.toLowerCase() || type.toLowerCase()
|
||||
)}
|
||||
type="button"
|
||||
onClick={() => suggestTaxonomy('ai', type)}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<SparklesIcon />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{settings?.copilotEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action"
|
||||
title={localize(
|
||||
@@ -317,7 +334,7 @@ const TagPicker: React.FunctionComponent<ITagPickerProps> = ({
|
||||
label?.toLowerCase() || type.toLowerCase()
|
||||
)}
|
||||
type="button"
|
||||
onClick={() => suggestTaxonomy(type)}
|
||||
onClick={() => suggestTaxonomy('copilot', type)}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<CopilotIcon />
|
||||
|
||||
@@ -105,11 +105,13 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const suggestDescription = () => {
|
||||
const suggestDescription = (type: 'ai' | 'copilot') => {
|
||||
setLoading(localize(LocalizationKey.panelFieldsTextFieldAiGenerate));
|
||||
|
||||
messageHandler
|
||||
.request<string>(CommandToCode.copilotSuggestDescription)
|
||||
.request<string>(
|
||||
type === 'copilot' ? CommandToCode.copilotSuggestDescription : CommandToCode.aiSuggestDescription
|
||||
)
|
||||
.then((suggestion) => {
|
||||
setLoading(undefined);
|
||||
|
||||
@@ -130,12 +132,24 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{settings?.aiEnabled && settings?.copilotEnabled && (
|
||||
{settings?.aiEnabled && settings.seo.descriptionField === name && (
|
||||
<button
|
||||
className="metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50"
|
||||
title={localize(LocalizationKey.panelFieldsTextFieldAiMessage, label?.toLowerCase())}
|
||||
type="button"
|
||||
onClick={() => suggestDescription('ai')}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<SparklesIcon />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{settings?.copilotEnabled && (
|
||||
<button
|
||||
className="metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50"
|
||||
title={localize(LocalizationKey.panelFieldsTextFieldCopilotMessage, label?.toLowerCase())}
|
||||
type="button"
|
||||
onClick={() => settings.seo.descriptionField === name ? suggestDescription() : suggestTitle()}
|
||||
onClick={() => settings.seo.descriptionField === name ? suggestDescription('copilot') : suggestTitle()}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<CopilotIcon />
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface ISeoKeywordInfoProps {
|
||||
content: string;
|
||||
wordCount?: number;
|
||||
headings?: string[];
|
||||
firstParagraph?: string;
|
||||
}
|
||||
|
||||
const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
@@ -26,7 +27,8 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
slug,
|
||||
content,
|
||||
wordCount,
|
||||
headings
|
||||
headings,
|
||||
firstParagraph
|
||||
}: React.PropsWithChildren<ISeoKeywordInfoProps>) => {
|
||||
|
||||
const density = () => {
|
||||
@@ -90,9 +92,10 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
(slug.toLowerCase().includes(keyword.toLowerCase()) ||
|
||||
slug.toLowerCase().includes(keyword.replace(/ /g, '-').toLowerCase())),
|
||||
content: !!content && content.toLowerCase().includes(keyword.toLowerCase()),
|
||||
heading: checkHeadings()
|
||||
heading: checkHeadings(),
|
||||
firstParagraph: !!firstParagraph && firstParagraph.toLowerCase().includes(keyword.toLowerCase())
|
||||
};
|
||||
}, [title, description, slug, content, headings, wordCount]);
|
||||
}, [title, description, slug, content, headings, wordCount, firstParagraph]);
|
||||
|
||||
const tooltipContent = React.useMemo(() => {
|
||||
return (
|
||||
@@ -102,7 +105,8 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
<span className='inline-flex items-center gap-1'><ValidInfo isValid={checks.description} /> {localize(LocalizationKey.commonDescription)}</span><br />
|
||||
<span className='inline-flex items-center gap-1'><ValidInfo isValid={checks.slug} /> {localize(LocalizationKey.commonSlug)}</span><br />
|
||||
<span className='inline-flex items-center gap-1'><ValidInfo isValid={checks.content} /> {localize(LocalizationKey.panelSeoKeywordInfoValidInfoContent)}</span><br />
|
||||
<span className='inline-flex items-center gap-1'><ValidInfo isValid={!!checks.heading} /> {localize(LocalizationKey.panelSeoKeywordInfoValidInfoLabel)}</span>
|
||||
<span className='inline-flex items-center gap-1'><ValidInfo isValid={!!checks.heading} /> {localize(LocalizationKey.panelSeoKeywordInfoValidInfoLabel)}</span><br />
|
||||
<span className='inline-flex items-center gap-1'><ValidInfo isValid={checks.firstParagraph} /> {localize(LocalizationKey.panelSeoKeywordInfoValidInfoFirstParagraph)}</span>
|
||||
</>
|
||||
)
|
||||
}, [checks]);
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface ISeoKeywordsProps {
|
||||
content: string;
|
||||
headings?: string[];
|
||||
wordCount?: number;
|
||||
firstParagraph?: string;
|
||||
}
|
||||
|
||||
const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
|
||||
@@ -96,6 +96,7 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
headings={metadata?.articleDetails?.headingsText}
|
||||
wordCount={metadata?.articleDetails?.wordCount}
|
||||
content={metadata?.articleDetails?.content}
|
||||
firstParagraph={metadata?.articleDetails?.firstParagraph}
|
||||
/>
|
||||
|
||||
<FieldBoundary fieldName={`Keywords`}>
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from './Credentials';
|
||||
export * from './ModeSwitch';
|
||||
export * from './PagesParser';
|
||||
export * from './PinnedItems';
|
||||
export * from './SponsorAI';
|
||||
export * from './Terminal';
|
||||
|
||||
Reference in New Issue
Block a user