Merge pull request #877 from estruyf/beta

v10.5.1 changes
This commit is contained in:
Elio Struyf
2024-10-23 17:37:17 +02:00
committed by GitHub
16 changed files with 317 additions and 466 deletions

View File

@@ -1,5 +1,17 @@
# Change Log
## [10.5.1] - 2024-10-23
### 🎨 Enhancements
- [#873](https://github.com/estruyf/vscode-front-matter/issues/873): Add retry logic to get the AI model for calling GitHub Copilot
### 🐞 Fixes
- [#872](https://github.com/estruyf/vscode-front-matter/issues/872): Check the default field value as well for the field's `when` clause
- [#874](https://github.com/estruyf/vscode-front-matter/issues/874): Fix media snippet markup insertion to article content's
- [#875](https://github.com/estruyf/vscode-front-matter/issues/875): Clean up the exclamation marks from the file name when creating new content
## [10.5.0] - 2024-10-21 - [Release notes](https://beta.frontmatter.codes/updates/v10.5.0)
### 🎨 Enhancements

View File

@@ -131,7 +131,8 @@
}
.article__tags__dropbox.open {
border: 1px solid rgba(0, 0, 0, 0.9);
border: 1px solid var(--vscode-focusBorder);
width: 100%;
}
.article__tags ul {

671
package-lock.json generated

File diff suppressed because it is too large Load Diff

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.5.0",
"version": "10.5.1",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {

View File

@@ -18,7 +18,7 @@ import {
SETTING_MEDIA_SUPPORTED_MIMETYPES
} from '../constants';
import { SortingOption } from '../dashboardWebView/models';
import { MediaInfo, MediaPaths, SortOrder, SortType } from '../models';
import { BlockFieldData, MediaInfo, MediaPaths, SortOrder, SortType } from '../models';
import { basename, join, parse, dirname, relative } from 'path';
import { statSync } from 'fs';
import { Uri, workspace, window, Position } from 'vscode';
@@ -376,7 +376,18 @@ export class MediaHelpers {
* Insert an image into the front matter or contents
* @param data
*/
public static async insertMediaToMarkdown(data: any) {
public static async insertMediaToMarkdown(data: {
file: string;
relPath: string;
snippet: string;
position: Position;
title?: string;
alt?: string;
caption?: string;
fieldName: string;
parents: string[];
blockData: BlockFieldData;
}) {
if (data?.file && data?.relPath) {
await EditorHelper.showFile(data.file);
Dashboard.resetViewData();
@@ -444,7 +455,7 @@ export class MediaHelpers {
const docType = Wysiwyg.getDocType(filePath);
let snippet = data.snippet || '';
if (!data.Snippet) {
if (!snippet) {
if (docType === 'markdown') {
snippet = `${isFile ? '' : '!'}[${caption}](${FrameworkDetector.relAssetPathUpdate(
relPath,

View File

@@ -1,6 +1,6 @@
const illegalRe = /[/?<>\\:*|"]/g;
const illegalRe = /[/?<>\\:*|"!.,;{}[\]()_+=~`@#$%^&]/g;
// eslint-disable-next-line no-control-regex
const controlRe = /[\x00-\x1F\x80-\x9F]/g;
const controlRe = /[\x00-\x1f\x80-\x9f]/g;
const reservedRe = /^\.+$/;
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
const windowsTrailingRe = /[. ]+$/;
@@ -9,6 +9,7 @@ function sanitize(input: string, replacement: string) {
if (typeof input !== 'string') {
throw new Error('Input must be string');
}
const sanitized = input
.replace(illegalRe, replacement)
.replace(controlRe, replacement)

View File

@@ -115,7 +115,12 @@ export class DataListener extends BaseListener {
}
private static async copilotSuggestTitle(command: string, requestId?: string, title?: string) {
if (!command || !requestId || !title) {
if (!command || !requestId) {
return;
}
if (!title) {
this.sendRequestError(command, requestId, 'No title provided');
return;
}

View File

@@ -15,7 +15,8 @@ export interface IFieldCollectionProps {
parentFields: string[],
blockData?: BlockFieldData,
onFieldUpdate?: (field: string | undefined, value: any, parents: string[]) => void,
parentBlock?: string | null
parentBlock?: string | null,
triggerUpdateOnDefault?: boolean
) => (JSX.Element | null)[] | undefined;
onChange: (field: string | undefined, value: any, parents: string[]) => void;
}

View File

@@ -55,8 +55,10 @@ export interface IWrapperFieldProps {
parentFields: string[],
blockData?: BlockFieldData,
onFieldUpdate?: (field: string | undefined, value: any, parents: string[]) => void,
parentBlock?: string | null
parentBlock?: string | null,
triggerUpdateOnDefault?: boolean
) => (JSX.Element | null)[] | undefined;
triggerUpdateOnDefault?: boolean;
}
export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
@@ -72,7 +74,8 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
parentBlock,
onSendUpdate,
unsetFocus,
renderFields
renderFields,
triggerUpdateOnDefault
}: React.PropsWithChildren<IWrapperFieldProps>) => {
const [fieldValue, setFieldValue] = useState<any | undefined>(undefined);
const [customFields, setCustomFields] = useState<{
@@ -110,7 +113,9 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
value = getDate(value) || null;
}
//onSendUpdate(field.name, value, parentFields);
if (triggerUpdateOnDefault) {
onSendUpdate(field.name, value, parentFields);
}
}
// Check if the field value contains a placeholder
@@ -140,7 +145,7 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
}
}
}
}, [field, parent]);
}, [field, parent, triggerUpdateOnDefault]);
useEffect(() => {
if (window.fmExternal && window.fmExternal.getCustomFields) {
@@ -437,7 +442,9 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
subMetadata,
[...parentFields, field.name],
blockData,
onSendUpdate
onSendUpdate,
null,
true
)}
</div>
</FieldBoundary>

View File

@@ -61,7 +61,8 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
blockData?: BlockFieldData,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onFieldUpdate?: (field: string | undefined, value: any, parents: string[]) => void,
parentBlock?: string | null
parentBlock?: string | null,
triggerUpdateOnDefault?: boolean
): (JSX.Element | null)[] | undefined => {
if (!ctFields || !settings) {
return;
@@ -83,6 +84,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
onSendUpdate={onFieldUpdate || onSendUpdate}
unsetFocus={unsetFocus}
renderFields={renderFields}
triggerUpdateOnDefault={triggerUpdateOnDefault}
/>
));
};

View File

@@ -1,7 +1,7 @@
import { useCallback } from 'react';
export default function useDropdownStyle(inputRef: React.MutableRefObject<HTMLInputElement | null>, inputHeight?: string) {
const bottomStyle = `calc(100% - ${inputHeight || '38px'})`;
const bottomStyle = `calc(100% - ${inputHeight || '5px'})`;
const listItemHeight = 28;
const getDropdownStyle = useCallback((isOpen) => {

View File

@@ -839,6 +839,8 @@ vscode-divider {
bottom: 0;
width: auto;
border-radius: 0 0.25rem 0.25rem 0;
&:disabled {
background: none;
filter: brightness(100%);
@@ -947,6 +949,8 @@ vscode-divider {
display: flex;
align-items: center;
border-radius: 0 0.25rem 0.25rem 0;
span {
margin-right: 0.5rem;
font-size: 0.8rem;

View File

@@ -4,7 +4,8 @@ import {
LanguageModelChatResponse,
extensions,
lm,
version as VscodeVersion
version as VscodeVersion,
LanguageModelChat
} from 'vscode';
import { Logger, Settings, TaxonomyHelper } from '../helpers';
import {
@@ -14,6 +15,7 @@ import {
} from '../constants';
import { TagType } from '../panelWebView/TagType';
import { TaxonomyType } from '../models';
import { sleep } from '../utils';
export class Copilot {
private static personality =
@@ -51,7 +53,7 @@ export class Copilot {
LanguageModelChatMessage.User(`The title of the blog post is """${title}""".`)
];
const chatResponse = await this.getChatResponse(messages);
const chatResponse = await Copilot.getChatResponse(messages);
if (!chatResponse) {
return;
}
@@ -100,7 +102,7 @@ Response format: a single string wrapped in double quotes, e.g., "Boost your web
);
}
const chatResponse = await this.getChatResponse(messages);
const chatResponse = await Copilot.getChatResponse(messages);
if (!chatResponse) {
return;
@@ -179,7 +181,7 @@ Example: SEO, website optimization, digital marketing.`
);
}
const chatResponse = await this.getChatResponse(messages);
const chatResponse = await Copilot.getChatResponse(messages);
if (!chatResponse) {
return;
@@ -211,6 +213,9 @@ Example: SEO, website optimization, digital marketing.`
try {
const model = await this.getModel();
if (!model) {
return;
}
chatResponse = await model.sendRequest(messages, {}, new CancellationTokenSource().token);
} catch (err) {
Logger.error(`Copilot:getChatResponse:: ${(err as Error).message}`);
@@ -229,7 +234,7 @@ Example: SEO, website optimization, digital marketing.`
* Retrieves the chat model for the Copilot service.
* @returns A Promise that resolves to the chat model.
*/
private static async getModel() {
private static async getModel(retry = 0): Promise<LanguageModelChat | undefined> {
// const models = await lm.selectChatModels();
// console.log(models);
const [model] = await lm.selectChatModels({
@@ -237,6 +242,11 @@ Example: SEO, website optimization, digital marketing.`
family: Settings.get<string>(SETTING_COPILOT_FAMILY) || 'gpt-4o-mini'
});
if ((!model || !model.sendRequest) && retry <= 5) {
await sleep(1000);
return Copilot.getModel(retry + 1);
}
return model;
}
}

View File

@@ -22,7 +22,17 @@ export const fieldWhenClause = (field: Field, parent: IMetadata, allFields?: Fie
}
}
const whenValue = parent[when.fieldRef];
let whenValue = parent[when.fieldRef];
// If the value is not yet set, check if the field has a default value.
if (
typeof whenValue === 'undefined' &&
parentField &&
typeof parentField.default !== 'undefined'
) {
whenValue = parentField.default as string | IMetadata | string[] | null;
}
if (when.caseSensitive || typeof when.caseSensitive === 'undefined') {
return caseSensitive(when, field, whenValue);
} else {
@@ -61,7 +71,7 @@ const caseInsensitive = (
*/
const caseSensitive = (
when: WhenClause,
field: Field,
_: Field,
whenValue: string | IMetadata | string[] | null
) => {
switch (when.operator) {

View File

@@ -20,6 +20,7 @@ export * from './readdirAsync';
export * from './renameAsync';
export * from './rmdirAsync';
export * from './sentryInit';
export * from './sleep';
export * from './sortPages';
export * from './unlinkAsync';
export * from './writeFileAsync';

1
src/utils/sleep.ts Normal file
View File

@@ -0,0 +1 @@
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));