mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
#846 - Add GH Copilot action for title field
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
- [#833](https://github.com/estruyf/vscode-front-matter/issues/833): Added support for Asciidoc files
|
||||
- [#834](https://github.com/estruyf/vscode-front-matter/issues/834): Added the ability to create new data files for a data folder
|
||||
- [#846](https://github.com/estruyf/vscode-front-matter/issues/846): Added GitHub Copilot action for title field
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
|
||||
@@ -93,46 +93,12 @@ export class Questions {
|
||||
}
|
||||
}
|
||||
|
||||
if (title && aiTitles && aiTitles.length > 0) {
|
||||
const options: QuickPickItem[] = [
|
||||
{
|
||||
label: `✏️ ${l10n.t(
|
||||
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
{
|
||||
label: title
|
||||
},
|
||||
{
|
||||
label: `🤖 ${l10n.t(
|
||||
isCopilotInstalled
|
||||
? LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator
|
||||
: LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
...aiTitles.map((d: string) => ({
|
||||
label: d
|
||||
}))
|
||||
];
|
||||
|
||||
const selectedTitle = await window.showQuickPick(options, {
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (selectedTitle) {
|
||||
title = selectedTitle.label;
|
||||
} else if (!selectedTitle) {
|
||||
// Reset the title, so the user can enter their own title
|
||||
title = undefined;
|
||||
}
|
||||
} else if (!title && showWarning) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
|
||||
return;
|
||||
}
|
||||
title = await this.pickTitleSuggestions(
|
||||
title,
|
||||
aiTitles || [],
|
||||
isCopilotInstalled,
|
||||
showWarning
|
||||
);
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
@@ -152,6 +118,56 @@ export class Questions {
|
||||
return title;
|
||||
}
|
||||
|
||||
public static async pickTitleSuggestions(
|
||||
title: string | undefined,
|
||||
aiTitles: string[],
|
||||
isCopilotInstalled: boolean,
|
||||
showWarning: boolean = true
|
||||
): Promise<string | undefined> {
|
||||
if (title && aiTitles && aiTitles.length > 0) {
|
||||
const options: QuickPickItem[] = [
|
||||
{
|
||||
label: `✏️ ${l10n.t(
|
||||
LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickTitleSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
{
|
||||
label: title
|
||||
},
|
||||
{
|
||||
label: `🤖 ${l10n.t(
|
||||
isCopilotInstalled
|
||||
? LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickCopilotSeparator
|
||||
: LocalizationKey.helpersQuestionsContentTitleAiInputQuickPickAiSeparator
|
||||
)}`,
|
||||
kind: QuickPickItemKind.Separator
|
||||
},
|
||||
...aiTitles.map((d: string) => ({
|
||||
label: d
|
||||
}))
|
||||
];
|
||||
|
||||
const selectedTitle = await window.showQuickPick(options, {
|
||||
title: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectTitle),
|
||||
placeHolder: l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputSelectPlaceholder),
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (selectedTitle) {
|
||||
title = selectedTitle.label;
|
||||
} else if (!selectedTitle) {
|
||||
// Reset the title, so the user can enter their own title
|
||||
title = undefined;
|
||||
}
|
||||
} else if (!title && showWarning) {
|
||||
Notifications.warning(l10n.t(LocalizationKey.helpersQuestionsContentTitleAiInputWarning));
|
||||
return;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the folder for your content creation
|
||||
* @param showWarning
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
processArticlePlaceholdersFromData,
|
||||
processTimePlaceholders,
|
||||
processFmPlaceholders,
|
||||
parseWinPath
|
||||
parseWinPath,
|
||||
Questions
|
||||
} from '../../helpers';
|
||||
import {
|
||||
COMMAND_NAME,
|
||||
@@ -107,6 +108,24 @@ export class DataListener extends BaseListener {
|
||||
case CommandToCode.copilotSuggestDescription:
|
||||
this.copilotSuggestDescription(msg.command, msg.requestId);
|
||||
break;
|
||||
case CommandToCode.copilotSuggestTitle:
|
||||
this.copilotSuggestTitle(msg.command, msg.requestId, msg.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async copilotSuggestTitle(command: string, requestId?: string, title?: string) {
|
||||
if (!command || !requestId || !title) {
|
||||
return;
|
||||
}
|
||||
|
||||
const aiTitles = await Copilot.suggestTitles(title);
|
||||
title = await Questions.pickTitleSuggestions(title, aiTitles || [], true);
|
||||
|
||||
if (title) {
|
||||
this.sendRequest(command, requestId, title);
|
||||
} else {
|
||||
this.sendRequestError(command, requestId, 'Failed to suggest title');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export enum CommandToCode {
|
||||
stopServer = 'stop-server',
|
||||
aiSuggestTaxonomy = 'ai-suggest-taxonomy',
|
||||
aiSuggestDescription = 'ai-suggest-description',
|
||||
copilotSuggestTitle = 'copilot-suggest-title',
|
||||
copilotSuggestDescription = 'copilot-suggest-description',
|
||||
copilotSuggestTaxonomy = 'copilot-suggest-taxonomy',
|
||||
searchByType = 'search-by-type',
|
||||
|
||||
@@ -93,6 +93,25 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
}
|
||||
}, [showRequiredState, isValid]);
|
||||
|
||||
|
||||
const suggestTitle = () => {
|
||||
setLoading(localize(LocalizationKey.panelFieldsTextFieldAiGenerate));
|
||||
|
||||
messageHandler
|
||||
.request<string>(CommandToCode.copilotSuggestTitle, text)
|
||||
.then((suggestion) => {
|
||||
setLoading(undefined);
|
||||
|
||||
if (suggestion) {
|
||||
setText(suggestion);
|
||||
onChange(suggestion);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(undefined);
|
||||
});
|
||||
};
|
||||
|
||||
const suggestDescription = (type: 'ai' | 'copilot') => {
|
||||
setLoading(localize(LocalizationKey.panelFieldsTextFieldAiGenerate));
|
||||
|
||||
@@ -114,13 +133,13 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
};
|
||||
|
||||
const actionElement = useMemo(() => {
|
||||
if (settings.seo.descriptionField !== name) {
|
||||
if (settings.seo.descriptionField !== name && settings.seo.titleField !== name) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{settings?.aiEnabled && (
|
||||
{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())}
|
||||
@@ -137,7 +156,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
className="metadata_field__title__action inline-block text-[var(--vscode-editor-foreground)] disabled:opacity-50"
|
||||
title={localize(LocalizationKey.panelFieldsTextFieldCopilotMessage, label?.toLowerCase())}
|
||||
type="button"
|
||||
onClick={() => suggestDescription('copilot')}
|
||||
onClick={() => settings.seo.descriptionField === name ? suggestDescription('copilot') : suggestTitle()}
|
||||
disabled={!!loading}
|
||||
>
|
||||
<CopilotIcon />
|
||||
@@ -145,7 +164,7 @@ export const TextField: React.FunctionComponent<ITextFieldProps> = ({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [settings?.aiEnabled, settings?.copilotEnabled, name, actions, loading]);
|
||||
}, [settings?.aiEnabled, settings?.copilotEnabled, settings?.seo, name, actions, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (text !== value && (lastUpdated === null || Date.now() - DEBOUNCE_TIME > lastUpdated)) {
|
||||
|
||||
@@ -47,7 +47,7 @@ export class Copilot {
|
||||
|
||||
IMPORTANT: You are only allowed to respond with a text that should not exceed ${chars} characters in length.
|
||||
|
||||
Desired format: just a string, e.g. "My first blog post". Each suggestion is separated by a new line.`
|
||||
Desired format: just a string and wrapped in double quotes, e.g. "My first blog post". Each suggestion is separated by a new line.`
|
||||
),
|
||||
LanguageModelChatMessage.User(`The title of the blog post is """${title}""".`)
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user