Compare commits

..

1 Commits

Author SHA1 Message Date
Elio Struyf
301814bcdd #816 - Sample implementation 2024-06-06 17:13:03 +02:00
23 changed files with 394 additions and 297 deletions

8
.vscode/launch.json vendored
View File

@@ -10,9 +10,7 @@
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}", "--disable-extension=eliostruyf.vscode-front-matter"
],
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: build:ext"
},
@@ -21,9 +19,7 @@
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}", "--disable-extension=eliostruyf.vscode-front-matter"
],
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]

10
.vscode/settings.json vendored
View File

@@ -1,15 +1,5 @@
// Place your settings in this file to overwrite default and user settings.
{
"commitHelper.messages": [
{
"type": "👨‍💻 apps",
"values": ["#search", "#profile"]
},
{
"type": "⚙️ tasks",
"values": ["#build", "#deploy", "#skip"]
}
],
"workbench.colorCustomizations": {
"titleBar.activeBackground": "#15c2cb",
"titleBar.inactiveBackground": "#44ffd299",

View File

@@ -1,10 +1,6 @@
# Change Log
## [10.2.1] - 2024-08-08
- [#820](https://github.com/estruyf/vscode-front-matter/issues/820): Update API links to the new API URL
## [10.2.0] - 2024-06-12 - [Release notes](https://beta.frontmatter.codes/updates/v10.2.0)
## [10.2.0] - 2024-xx-xx
### ✨ New features
@@ -13,15 +9,15 @@
### 🎨 Enhancements
- [#441](https://github.com/estruyf/vscode-front-matter/issues/441): Show input descriptions for snippet and data forms
- [#442](https://github.com/estruyf/vscode-front-matter/issues/442): Hide sidebar on data view when data file is selected + show dropdown of data files
- [#442](https://github.com/estruyf/vscode-front-matter/issues/442): Hide sidebar on data view when data file is selecte + show dropdown of data files
- [#788](https://github.com/estruyf/vscode-front-matter/issues/788): Show a warning on setting update when it exists in an extended configuration
- [#798](https://github.com/estruyf/vscode-front-matter/issues/798): Changed dialog to slide-over for the snippet forms
- [#799](https://github.com/estruyf/vscode-front-matter/issues/799): Added `frontMatter.logging` setting to define the logging output. Options are `info`, `warn`, `error`, and `verbose`. The default is `info`.
- [#799](https://github.com/estruyf/vscode-front-matter/issues/799): Added `frontMatter.logging` setting to define the logging output. Options are `info`, `warn`, `error`, and `verbose`. Default is `info`.
- [#800](https://github.com/estruyf/vscode-front-matter/issues/800): Add colors for the Front Matter CMS output
- [#808](https://github.com/estruyf/vscode-front-matter/issues/808): Add support to generate field groups and `block` fields in content type generation
- [#810](https://github.com/estruyf/vscode-front-matter/issues/810): Update the tab title based on the view
- [#811](https://github.com/estruyf/vscode-front-matter/issues/811): Added `panel.gitActions` view mode option to hide the Git actions in the panel
- [#812](https://github.com/estruyf/vscode-front-matter/issues/812): Added the `{{locale}}` placeholder, which can be used in the `previewPath` property
- [#812](https://github.com/estruyf/vscode-front-matter/issues/812): Added the `{{locale}}` placeholder which can be used in the `previewPath` property
### ⚡️ Optimizations

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1250 1250" style="enable-background:new 0 0 1250 1250;" xml:space="preserve">
<path fill="#02aeb7" d="M316,1082.3H119.4V151.2h347.5v218.9H316v135.7h140.5v210.5H316V1082.3z"/>
<path fill="#02aeb7" d="M602.2,151.2H704l77.7,379.9c9.5,47.4,18.1,95,26,142.6s15,97.6,21.4,149.8c0.7-6.8,1.3-12.1,1.7-16
c0.2-2.7,0.6-5.5,1.1-8.2l16.6-106.7l14.9-101.3l13.2-66.9l69.2-373.3h102.9l81.2,931.1h-113.6l-19.9-316c-0.8-16.1-1.4-29.9-2-41.6
c-0.6-11.7-0.9-21.3-0.9-29L988.3,571l-2.8-114.6c0-0.8,0-2.5-0.3-5.1s-0.5-6.1-0.9-10.6l-2.8,18.7c-3,22.1-5.8,41.4-8.3,57.9
s-4.7,30.3-6.6,41.6l-15.1,84.9l-5.7,32l-74.3,406.4h-80.1l-69.7-351c-9.5-46.2-17.9-93.1-25.4-140.8s-14.2-97.6-20.3-149.9
l-34.3,641.6H529.6L602.2,151.2z"/>
<rect x="119.4" y="0.1" fill="#02aeb7" width="184" height="64.7"/>
<rect x="395.7" y="0.1" fill="#02aeb7" width="184" height="64.7"/>
<rect x="675.3" y="0.1" fill="#02aeb7" width="184" height="64.7"/>
<rect x="119.4" y="1184.7" fill="#02aeb7" width="184" height="64.7"/>
<rect x="395.7" y="1184.7" fill="#02aeb7" width="184" height="64.7"/>
<rect x="675.3" y="1184.7" fill="#02aeb7" width="184" height="64.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

14
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "vscode-front-matter-beta",
"version": "10.2.1",
"version": "10.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-front-matter-beta",
"version": "10.2.1",
"version": "10.2.0",
"license": "MIT",
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.0.6"
@@ -33,7 +33,7 @@
"@types/react": "17.0.0",
"@types/react-datepicker": "^4.8.0",
"@types/react-dom": "17.0.0",
"@types/vscode": "^1.73.0",
"@types/vscode": "^1.90.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vscode-elements/elements": "^1.2.0",
@@ -110,7 +110,7 @@
"yawn-yaml": "^1.5.0"
},
"engines": {
"vscode": "^1.73.0"
"vscode": "^1.90.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2084,9 +2084,9 @@
"dev": true
},
"node_modules/@types/vscode": {
"version": "1.86.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.86.0.tgz",
"integrity": "sha512-DnIXf2ftWv+9LWOB5OJeIeaLigLHF7fdXF6atfc7X5g2w/wVZBgk0amP7b+ub5xAuW1q7qP5YcFvOcit/DtyCQ==",
"version": "1.90.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.90.0.tgz",
"integrity": "sha512-oT+ZJL7qHS9Z8bs0+WKf/kQ27qWYR3trsXpq46YDjFqBsMLG4ygGGjPaJ2tyrH0wJzjOEmDyg9PDJBBhWg9pkQ==",
"dev": true
},
"node_modules/@types/vscode-webview": {

View File

@@ -3,15 +3,14 @@
"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.2.1",
"version": "10.2.0",
"preview": false,
"publisher": "eliostruyf",
"galleryBanner": {
"color": "#0e131f",
"theme": "dark"
},
"badges": [
{
"badges": [{
"description": "version",
"url": "https://img.shields.io/github/package-json/v/estruyf/vscode-front-matter?color=green&label=vscode-front-matter&style=flat-square",
"href": "https://github.com/estruyf/vscode-front-matter"
@@ -27,10 +26,13 @@
},
"qna": "https://github.com/estruyf/vscode-front-matter/discussions",
"engines": {
"vscode": "^1.73.0"
"vscode": "^1.90.0"
},
"l10n": "./l10n",
"categories": [
"AI",
"Chat",
"Visualization",
"Other"
],
"keywords": [
@@ -70,8 +72,22 @@
"**/.frontmatter/config/*.json": "jsonc"
}
},
"keybindings": [
{
"chatParticipants": [{
"id": "frontMatter.chat",
"name": "fm",
"fullName": "Front Matter",
"description": "Front Matter CMS chat",
"isSticky": true,
"commands": [{
"name": "docs",
"description": "Ask a question about Front Matter CMS and we will try to help you out."
}, {
"name": "create",
"description": "Create a new Front Matter CMS project.",
"isSticky": true
}]
}],
"keybindings": [{
"command": "frontMatter.dashboard",
"key": "alt+d"
},
@@ -95,23 +111,19 @@
}
],
"viewsContainers": {
"activitybar": [
{
"id": "frontmatter-explorer",
"title": "FM",
"icon": "$(fm-logo)"
}
]
"activitybar": [{
"id": "frontmatter-explorer",
"title": "FM",
"icon": "$(fm-logo)"
}]
},
"views": {
"frontmatter-explorer": [
{
"id": "frontMatter.explorer",
"name": "Front Matter",
"icon": "$(fm-logo)",
"type": "webview"
}
]
"frontmatter-explorer": [{
"id": "frontMatter.explorer",
"name": "Front Matter",
"icon": "$(fm-logo)",
"type": "webview"
}]
},
"configuration": {
"title": "%settings.configuration.title%",
@@ -179,8 +191,7 @@
"frontMatter.content.defaultFileType": {
"type": "string",
"default": "md",
"oneOf": [
{
"oneOf": [{
"enum": [
"md",
"mdx"
@@ -196,8 +207,7 @@
"frontMatter.content.defaultSorting": {
"type": "string",
"default": "",
"oneOf": [
{
"oneOf": [{
"enum": [
"LastModifiedAsc",
"LastModifiedDesc",
@@ -549,8 +559,7 @@
"categories"
],
"markdownDescription": "%setting.frontMatter.content.filters.markdownDescription%",
"items": [
{
"items": [{
"type": "string",
"enum": [
"contentFolders",
@@ -623,8 +632,7 @@
"command": {
"$id": "#scriptCommand",
"type": "string",
"anyOf": [
{
"anyOf": [{
"enum": [
"node",
"bash",
@@ -820,8 +828,7 @@
"title",
"file"
],
"anyOf": [
{
"anyOf": [{
"required": [
"schema"
]
@@ -875,8 +882,7 @@
"id",
"path"
],
"anyOf": [
{
"anyOf": [{
"required": [
"schema"
]
@@ -1117,29 +1123,26 @@
}
}
},
"default": [
{
"name": "default",
"fileTypes": null,
"fields": [
{
"title": "Title",
"name": "title",
"type": "string"
},
{
"title": "Caption",
"name": "caption",
"type": "string"
},
{
"title": "Alt text",
"name": "alt",
"type": "string"
}
]
}
],
"default": [{
"name": "default",
"fileTypes": null,
"fields": [{
"title": "Title",
"name": "title",
"type": "string"
},
{
"title": "Caption",
"name": "caption",
"type": "string"
},
{
"title": "Alt text",
"name": "alt",
"type": "string"
}
]
}],
"scope": "Media"
},
"frontMatter.media.supportedMimeTypes": {
@@ -1191,7 +1194,7 @@
"scope": "Site preview"
},
"frontMatter.preview.trailingSlash": {
"type": "boolean",
"type": "string",
"default": "",
"markdownDescription": "%setting.frontMatter.preview.trailingSlash.markdownDescription%",
"scope": "Site preview"
@@ -1375,8 +1378,7 @@
"default": "",
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%",
"not": {
"anyOf": [
{
"anyOf": [{
"const": ""
},
{
@@ -1570,8 +1572,7 @@
"type",
"name"
],
"allOf": [
{
"allOf": [{
"if": {
"properties": {
"type": {
@@ -1783,51 +1784,48 @@
"fields"
]
},
"default": [
{
"name": "default",
"pageBundle": false,
"fields": [
{
"title": "Title",
"name": "title",
"type": "string"
},
{
"title": "Description",
"name": "description",
"type": "string"
},
{
"title": "Publishing date",
"name": "date",
"type": "datetime",
"default": "{{now}}",
"isPublishDate": true
},
{
"title": "Content preview",
"name": "preview",
"type": "image"
},
{
"title": "Is in draft",
"name": "draft",
"type": "boolean"
},
{
"title": "Tags",
"name": "tags",
"type": "tags"
},
{
"title": "Categories",
"name": "categories",
"type": "categories"
}
]
}
],
"default": [{
"name": "default",
"pageBundle": false,
"fields": [{
"title": "Title",
"name": "title",
"type": "string"
},
{
"title": "Description",
"name": "description",
"type": "string"
},
{
"title": "Publishing date",
"name": "date",
"type": "datetime",
"default": "{{now}}",
"isPublishDate": true
},
{
"title": "Content preview",
"name": "preview",
"type": "image"
},
{
"title": "Is in draft",
"name": "draft",
"type": "boolean"
},
{
"title": "Tags",
"name": "tags",
"type": "tags"
},
{
"title": "Categories",
"name": "categories",
"type": "categories"
}
]
}],
"scope": "Taxonomy"
},
"frontMatter.taxonomy.customTaxonomy": {
@@ -1840,8 +1838,7 @@
"type": "string",
"description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%",
"not": {
"anyOf": [
{
"anyOf": [{
"const": ""
},
{
@@ -2036,8 +2033,7 @@
}
}
},
"commands": [
{
"commands": [{
"command": "frontMatter.project.switch",
"title": "%command.frontMatter.project.switch%",
"category": "Front Matter",
@@ -2363,21 +2359,16 @@
}
}
],
"submenus": [
{
"id": "frontmatter.submenu",
"label": "Front Matter"
}
],
"submenus": [{
"id": "frontmatter.submenu",
"label": "Front Matter"
}],
"menus": {
"webview/context": [
{
"command": "workbench.action.webview.openDeveloperTools",
"when": "frontMatter:isDevelopment"
}
],
"editor/title": [
{
"webview/context": [{
"command": "workbench.action.webview.openDeveloperTools",
"when": "frontMatter:isDevelopment"
}],
"editor/title": [{
"command": "frontMatter.markup.heading",
"group": "navigation@-133",
"when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg"
@@ -2463,14 +2454,11 @@
"when": "resourceFilename == 'frontmatter.json'"
}
],
"explorer/context": [
{
"submenu": "frontmatter.submenu",
"group": "frontmatter@1"
}
],
"frontmatter.submenu": [
{
"explorer/context": [{
"submenu": "frontmatter.submenu",
"group": "frontmatter@1"
}],
"frontmatter.submenu": [{
"command": "frontMatter.createFromTemplate",
"when": "explorerResourceIsFolder",
"group": "frontmatter@1"
@@ -2486,8 +2474,7 @@
"group": "frontmatter@3"
}
],
"commandPalette": [
{
"commandPalette": [{
"command": "frontMatter.init",
"when": "frontMatterCanInit"
},
@@ -2664,8 +2651,7 @@
"when": "frontMatter:file:isValid == true"
}
],
"view/title": [
{
"view/title": [{
"command": "frontMatter.chatbot",
"group": "navigation@0",
"when": "view == frontMatter.explorer"
@@ -2697,16 +2683,13 @@
}
]
},
"languages": [
{
"id": "frontmatter.project.output",
"mimetypes": [
"text/x-code-output"
]
}
],
"grammars": [
{
"languages": [{
"id": "frontmatter.project.output",
"mimetypes": [
"text/x-code-output"
]
}],
"grammars": [{
"path": "./syntaxes/hugo.tmLanguage.json",
"scopeName": "frontmatter.markdown.hugo",
"injectTo": [
@@ -2719,48 +2702,45 @@
"path": "./syntaxes/frontmatter-output.tmLanguage.json"
}
],
"walkthroughs": [
{
"id": "frontmatter.welcome",
"title": "Get started with Front Matter",
"description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.",
"steps": [
{
"id": "frontmatter.welcome.init",
"title": "Get started",
"description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)",
"media": {
"markdown": "assets/walkthrough/get-started.md"
},
"completionEvents": [
"onContext:frontMatterInitialized"
]
"walkthroughs": [{
"id": "frontmatter.welcome",
"title": "Get started with Front Matter",
"description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.",
"steps": [{
"id": "frontmatter.welcome.init",
"title": "Get started",
"description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)",
"media": {
"markdown": "assets/walkthrough/get-started.md"
},
{
"id": "frontmatter.welcome.documentation",
"title": "Documentation",
"description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)",
"media": {
"markdown": "assets/walkthrough/documentation.md"
},
"completionEvents": [
"onLink:https://frontmatter.codes/docs"
]
"completionEvents": [
"onContext:frontMatterInitialized"
]
},
{
"id": "frontmatter.welcome.documentation",
"title": "Documentation",
"description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)",
"media": {
"markdown": "assets/walkthrough/documentation.md"
},
{
"id": "frontmatter.welcome.supporter",
"title": "Support the project",
"description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)",
"media": {
"markdown": "assets/walkthrough/support-the-project.md"
},
"completionEvents": [
"onLink:https://github.com/sponsors/estruyf"
]
}
]
}
]
"completionEvents": [
"onLink:https://frontmatter.codes/docs"
]
},
{
"id": "frontmatter.welcome.supporter",
"title": "Support the project",
"description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)",
"media": {
"markdown": "assets/walkthrough/support-the-project.md"
},
"completionEvents": [
"onLink:https://github.com/sponsors/estruyf"
]
}
]
}]
},
"scripts": {
"dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*",
@@ -2806,7 +2786,7 @@
"@types/react": "17.0.0",
"@types/react-datepicker": "^4.8.0",
"@types/react-dom": "17.0.0",
"@types/vscode": "^1.73.0",
"@types/vscode": "^1.90.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vscode-elements/elements": "^1.2.0",
@@ -2888,4 +2868,4 @@
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.0.6"
}
}
}

View File

@@ -27,7 +27,6 @@ import { CustomPlaceholder, Field } from '../models';
import { format } from 'date-fns';
import {
ArticleHelper,
Logger,
Settings,
SlugHelper,
processArticlePlaceholdersFromData,
@@ -134,7 +133,6 @@ export class Article {
private static async setLastModifiedDateInner(
document: TextDocument
): Promise<ParsedFrontMatter | undefined> {
Logger.verbose(`Article:setLastModifiedDateInner:Start`);
const article = ArticleHelper.getFrontMatterFromDocument(document);
// Only set the date, if there is already front matter set
@@ -144,16 +142,9 @@ export class Article {
const cloneArticle = Object.assign({}, article);
const dateField = await ArticleHelper.getModifiedDateField(article);
Logger.verbose(`Article:setLastModifiedDateInner:DateField - ${JSON.stringify(dateField)}`);
try {
const fieldName = dateField?.name || DefaultFields.LastModified;
const fieldValue = Article.formatDate(new Date(), dateField?.dateFormat);
cloneArticle.data[fieldName] = fieldValue;
Logger.verbose(
`Article:setLastModifiedDateInner:DateField name - ${fieldName} - value - ${fieldValue}`
);
Logger.verbose(`Article:setLastModifiedDateInner:End`);
cloneArticle.data[fieldName] = Article.formatDate(new Date(), dateField?.dateFormat);
return cloneArticle;
} catch (e: unknown) {
Notifications.error(
@@ -380,16 +371,11 @@ export class Article {
public static formatDate(dateValue: Date, fieldDateFormat?: string): string {
const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string;
Logger.verbose(`Article:formatDate:Start`);
if (fieldDateFormat) {
Logger.verbose(`Article:formatDate:FieldDateFormat - ${fieldDateFormat}`);
return format(dateValue, DateHelper.formatUpdate(fieldDateFormat) as string);
} else if (dateFormat && typeof dateFormat === 'string') {
Logger.verbose(`Article:formatDate:DateFormat - ${dateFormat}`);
return format(dateValue, DateHelper.formatUpdate(dateFormat) as string);
} else {
Logger.verbose(`Article:formatDate:toISOString - ${dateValue}`);
return typeof dateValue.toISOString === 'function'
? dateValue.toISOString()
: dateValue?.toString();

View File

@@ -22,16 +22,20 @@ export class Backers {
const githubAuth = await authentication.getSession('github', ['read:user'], { silent: true });
if (githubAuth && githubAuth.accessToken) {
try {
const response = await fetch(`https://api.frontmatter.codes/v2/backers`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
accept: 'application/json'
},
body: JSON.stringify({
token: githubAuth.accessToken
})
});
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 (response.ok) {
const prevData = await ext.getState<boolean>(CONTEXT.backer, 'global');

View File

@@ -1,14 +1,99 @@
import { Telemetry } from './../helpers/Telemetry';
import { TelemetryEvent, PreviewCommands, GeneralCommands } from './../constants';
import { TelemetryEvent, PreviewCommands, GeneralCommands, WEBSITE_LINKS, COMMAND_NAME } from './../constants';
import { join } from 'path';
import { commands, Uri, ViewColumn, window } from 'vscode';
import {
CancellationToken,
chat,
ChatContext,
ChatRequest,
ChatResponseStream,
commands,
LanguageModelChatMessage,
lm,
Uri,
ViewColumn,
window
} from 'vscode';
import { Extension } from '../helpers';
import { WebviewHelper } from '@estruyf/vscode';
import { getLocalizationFile } from '../utils/getLocalizationFile';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { Folders } from '.';
export class Chatbot {
private static company: string;
private static chatId: number;
public static async register() {
const fmChat = chat.createChatParticipant('frontMatter.chat', this.handler);
fmChat.iconPath = Uri.joinPath(
Extension.getInstance().extensionPath,
'/assets/icons/frontmatter-short-teal.svg'
);
}
private static async handler(
request: ChatRequest,
context: ChatContext,
stream: ChatResponseStream,
token: CancellationToken
) {
const { command, prompt } = request;
if (command === 'docs' && prompt) {
if (!this.chatId || !this.company) {
stream.progress('Initializing the Front Matter AI...');
await this.initChat();
}
if (!this.company || !this.chatId) {
this.showAiError(stream);
return;
}
stream.progress('Fetching information from the docs...');
const data: {
answer: string;
answerId: number;
sources: string[];
} = await this.retrieveDocs(prompt);
if (!data.answer) {
this.showAiError(stream);
return;
}
stream.markdown(data.answer);
} else if (command === 'create') {
stream.progress(`Checking your configuration...`);
const messages = [LanguageModelChatMessage.User(`You are a kind and helpful assistant named Front Matter AI. You aim to provide support in managing Front Matter CMS and its content. If you don't know the answer to a question, you will guide the user to the documentation (https://frontmatter.codes/docs). You can use the "https://frontmatter.codes/docs" link as a reference. When returning code you will surround it in a MD code block, not HTML.`)];
messages.push(LanguageModelChatMessage.User(request.prompt));
const [model] = await lm.selectChatModels({ vendor: 'copilot', family: 'gpt-4' });
try {
const chatResponse = await model.sendRequest(messages, {}, token);
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
stream.button({
command: COMMAND_NAME.createByContentType,
title: 'Create new content',
});
} catch (err) {
this.showAiError(stream);
return;
}
} else {
stream.markdown('Sorry, I do not understand that command. Check out the [Front Matter CMS](https://frontmatter.codes/docs) documentation for more information.')
}
}
/**
* Open the Chatbot in the editor
*/
@@ -119,4 +204,50 @@ export class Chatbot {
Telemetry.send(TelemetryEvent.openChatbot);
}
private static async initChat() {
const response = await fetch(`${WEBSITE_LINKS.root}/api/ai-init`);
if (!response.ok) {
return;
}
const data = await response.json();
if (!data.company || !data.chatId) {
return;
}
this.company = data.company;
this.chatId = data.chatId;
}
private static async retrieveDocs(prompt: string) {
if (!this.company || !this.chatId) {
return;
}
const response = await fetch(`${WEBSITE_LINKS.root}/api/ai-chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'accept': '*',
},
body: JSON.stringify({
company: this.company,
chatId: this.chatId,
question: prompt,
}),
});
if (!response.ok) {
return;
}
return await response.json();
}
private static showAiError(stream: ChatResponseStream) {
stream.markdown(`Sorry, I am not able to connect to the Front Matter AI service. Please try again later or check out the [Front Matter CMS](https://frontmatter.codes/docs) documentation.`);
}
}

View File

@@ -250,7 +250,9 @@ export class Preview {
// Check if there is a pathname defined on content folder level
const folders = await Folders.get();
if (folders.length > 0) {
for (const folder of folders) {
const foldersWithPath = folders.filter((folder) => folder.previewPath);
for (const folder of foldersWithPath) {
const folderPath = parseWinPath(folder.path);
if (filePath.startsWith(folderPath)) {
if (!selectedFolder || selectedFolder.path.length < folderPath.length) {

View File

@@ -1,12 +0,0 @@
import { FEATURE_FLAG } from './Features';
export const DEFAULT_PANEL_FEATURE_FLAGS = Object.values(FEATURE_FLAG.panel).filter(
(v) => v !== FEATURE_FLAG.panel.globalSettings
);
export const DEFAULT_DASHBOARD_FEATURE_FLAGS = [
FEATURE_FLAG.dashboard.data.view,
FEATURE_FLAG.dashboard.taxonomy.view,
FEATURE_FLAG.dashboard.snippets.view,
FEATURE_FLAG.dashboard.snippets.manage
];

View File

@@ -14,8 +14,8 @@ export const DOCS_SUBMODULES = 'https://frontmatter.codes/docs/git-integration#g
export const WEBSITE_LINKS = {
root: 'https://frontmatter.codes',
api: {
root: 'https://api.frontmatter.codes',
metrics: 'https://api.frontmatter.codes/metrics'
metrics: 'https://frontmatter.codes/api/metrics',
ai: 'https://frontmatter.codes/api/ai'
},
docs: {
dataDashboard: 'https://frontmatter.codes/docs/dashboard/datafiles-view',

View File

@@ -36,7 +36,7 @@ export const Chatbot: React.FunctionComponent<IChatbotProps> = ({ }: React.Props
setLocaleReady(true);
});
const initResponse = await fetch(`${aiUrl}/ai-init`);
const initResponse = await fetch(`${aiUrl}/api/ai-init`);
if (!initResponse.ok) {
return;
@@ -70,7 +70,7 @@ export const Chatbot: React.FunctionComponent<IChatbotProps> = ({ }: React.Props
return;
}
const response = await fetch(`${aiUrl}/ai-chat`, {
const response = await fetch(`${aiUrl}/api/ai-chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@@ -28,7 +28,7 @@ export const Feedback: React.FunctionComponent<IFeedbackProps> = ({
}, []);
const callVote = useCallback(async (vote: boolean) => {
await fetch(`${aiUrl}/ai-feedback`, {
await fetch(`${aiUrl}/api/ai-feedback`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@@ -9,7 +9,6 @@ import { Tab } from './Tab';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { PageIcon } from '../../../panelWebView/components/Icons';
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
export interface ITabsProps {
onNavigate: (navigationType: NavigationType) => void;
@@ -20,6 +19,12 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
}: React.PropsWithChildren<ITabsProps>) => {
const mode = useRecoilValue(ModeAtom);
const allDashboardVIews = [
FEATURE_FLAG.dashboard.snippets.view,
FEATURE_FLAG.dashboard.data.view,
FEATURE_FLAG.dashboard.taxonomy.view
];
return (
<ul
className="flex items-center justify-start h-full space-x-4"
@@ -38,7 +43,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
<span>{l10n.t(LocalizationKey.dashboardHeaderTabsMedia)}</span>
</Tab>
</li>
<FeatureFlag features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS} flag={FEATURE_FLAG.dashboard.snippets.view}>
<FeatureFlag features={mode?.features || [...allDashboardVIews]} flag={FEATURE_FLAG.dashboard.snippets.view}>
<li role="presentation">
<Tab navigationType={NavigationType.Snippets} onNavigate={onNavigate}>
<ScissorsIcon className={`h-4 w-auto mr-2`} />
@@ -46,7 +51,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
</Tab>
</li>
</FeatureFlag>
<FeatureFlag features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS} flag={FEATURE_FLAG.dashboard.data.view}>
<FeatureFlag features={mode?.features || [...allDashboardVIews]} flag={FEATURE_FLAG.dashboard.data.view}>
<li role="presentation">
<Tab navigationType={NavigationType.Data} onNavigate={onNavigate}>
<CircleStackIcon className={`h-4 w-auto mr-2`} />
@@ -54,7 +59,7 @@ export const Tabs: React.FunctionComponent<ITabsProps> = ({
</Tab>
</li>
</FeatureFlag>
<FeatureFlag features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS} flag={FEATURE_FLAG.dashboard.taxonomy.view}>
<FeatureFlag features={mode?.features || [...allDashboardVIews]} flag={FEATURE_FLAG.dashboard.taxonomy.view}>
<li role="presentation">
<Tab navigationType={NavigationType.Taxonomy} onNavigate={onNavigate}>
<TagIcon className={`h-4 w-auto mr-2`} />

View File

@@ -19,7 +19,6 @@ import { LocalizationKey } from '../../../localization';
import { FooterActions } from './FooterActions';
import { ItemMenu } from './ItemMenu';
import { SlideOver } from '../Modals/SlideOver';
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
export interface IItemProps {
snippetKey: string;
@@ -164,7 +163,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
</h2>
<FeatureFlag
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
features={mode?.features || []}
flag={FEATURE_FLAG.dashboard.snippets.manage}
alternative={
<ItemMenu
@@ -191,7 +190,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({
</div>
<FeatureFlag
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
features={mode?.features || []}
flag={FEATURE_FLAG.dashboard.snippets.manage}
alternative={
<FooterActions

View File

@@ -18,7 +18,6 @@ import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../../localization';
import { List } from '../Media/List';
import { SlideOver } from '../Modals/SlideOver';
import { DEFAULT_DASHBOARD_FEATURE_FLAGS } from '../../../constants/DefaultFeatureFlags';
export interface ISnippetsProps { }
@@ -97,9 +96,7 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (
return (
<PageLayout
header={
<FeatureFlag
features={mode?.features || DEFAULT_DASHBOARD_FEATURE_FLAGS}
flag={FEATURE_FLAG.dashboard.snippets.manage}>
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.dashboard.snippets.manage}>
<div
className={`py-3 px-4 flex items-center justify-between border-b border-[var(--frontmatter-border)]`}
aria-label={l10n.t(LocalizationKey.dashboardSnippetsViewSnippetsAriaLabel)}

View File

@@ -93,7 +93,7 @@ if (elm) {
render(
<I10nProvider>
<SettingsProvider
aiUrl={WEBSITE_LINKS.api.root}
aiUrl={WEBSITE_LINKS.root}
experimental={experimental === 'true'}
version={version || ""}>
<Chatbot />

View File

@@ -241,6 +241,9 @@ export async function activate(context: vscode.ExtensionContext) {
// Cache commands
Cache.registerCommands();
// GitHub Copilot integration
Chatbot.register();
// Subscribe all commands
subscriptions.push(insertTags, PanelView, insertCategories, collapseAll, fmStatusBarItem);

View File

@@ -71,7 +71,7 @@ export class PagesListener extends BaseListener {
// Optimize the list of recently changed files
DataListener.getFoldersAndFiles(doc.uri);
// Trigger the metadata update
this.watcherExec(doc.uri, 'save');
this.watcherExec(doc.uri);
}
});
}
@@ -103,9 +103,9 @@ export class PagesListener extends BaseListener {
false,
false
);
watcher.onDidCreate(async (uri: Uri) => this.watcherExec(uri, 'create'));
watcher.onDidChange(async (uri: Uri) => this.watcherExec(uri, 'change'));
watcher.onDidDelete(async (uri: Uri) => this.watcherExec(uri, 'delete'));
watcher.onDidCreate(async (uri: Uri) => this.watcherExec(uri));
watcher.onDidChange(async (uri: Uri) => this.watcherExec(uri));
watcher.onDidDelete(async (uri: Uri) => this.watcherExec(uri));
this.watchers[folderUri.fsPath] = watcher;
}
}
@@ -162,10 +162,10 @@ export class PagesListener extends BaseListener {
* Watcher for processing page updates
* @param file
*/
private static async watcherExec(file: Uri, type?: 'create' | 'change' | 'delete' | 'save') {
private static async watcherExec(file: Uri) {
const progressFile = async (file: Uri) => {
const ext = Extension.getInstance();
Logger.info(`File watcher execution for (${type}): ${file.fsPath}`);
Logger.info(`File watcher execution for: ${file.fsPath}`);
const pageIdx = this.lastPages.findIndex((p) => p.fmFilePath === file.fsPath);
if (pageIdx !== -1) {

View File

@@ -17,7 +17,6 @@ import { usePrevious } from './hooks/usePrevious';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
import { InitializeAction } from './components/InitializeAction';
import { DEFAULT_PANEL_FEATURE_FLAGS } from '../constants/DefaultFeatureFlags';
export interface IViewPanelProps { }
@@ -38,6 +37,10 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
const prevMediaSelection = usePrevious(mediaSelecting);
const [scrollY, setScrollY] = useState(0);
const allPanelValues = useMemo(() => {
return Object.values(FEATURE_FLAG.panel).filter(v => v !== FEATURE_FLAG.panel.globalSettings)
}, [FEATURE_FLAG.panel]);
const scollListener = useCallback((e: Event) => {
if (!mediaSelecting) {
setScrollY(window.scrollY);
@@ -45,7 +48,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
}, [mediaSelecting]);
const isSomethingShown = useMemo(() => {
const panelModeValues = (mode?.features || DEFAULT_PANEL_FEATURE_FLAGS).filter(v => v.startsWith('panel.'));
const panelModeValues = (mode?.features || [...allPanelValues]).filter(v => v.startsWith('panel.'));
if (panelModeValues.length === 0) {
return false;
@@ -122,19 +125,19 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
<InitializeAction settings={settings} />
<div className={`ext_actions`}>
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.gitActions}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.gitActions}>
<GitAction settings={settings} />
</FeatureFlag>
{!loading && (<CustomView metadata={metadata} />)}
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.globalSettings}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.globalSettings}>
<GlobalSettings settings={settings} isBase={!metadata} />
</FeatureFlag>
{
!loading && metadata && settings && settings.seo && (
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.seo}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.seo}>
<SeoStatus
seo={settings.seo}
data={metadata}
@@ -146,7 +149,7 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
}
{!loading && settings && (
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.actions}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.actions}>
<Actions
metadata={metadata}
settings={settings}
@@ -156,23 +159,23 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (
{
!loading && metadata && (
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.metadata}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.metadata}>
<Metadata
settings={settings}
metadata={metadata}
focusElm={focusElm}
unsetFocus={unsetFocus}
features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS}
features={mode?.features || [...allPanelValues]}
/>
</FeatureFlag>
)
}
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.recentlyModified}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.recentlyModified}>
<FolderAndFiles data={folderAndFiles} isBase={!metadata} />
</FeatureFlag>
<FeatureFlag features={mode?.features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.otherActions}>
<FeatureFlag features={mode?.features || [...allPanelValues]} flag={FEATURE_FLAG.panel.otherActions}>
<OtherActions settings={settings} isFile={!!metadata} isBase={!metadata} />
</FeatureFlag>

View File

@@ -12,7 +12,6 @@ import { FEATURE_FLAG, GeneralCommands } from '../../constants';
import { Messenger } from '@estruyf/vscode/dist/client';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../../localization';
import { DEFAULT_PANEL_FEATURE_FLAGS } from '../../constants/DefaultFeatureFlags';
export interface IMetadata {
[prop: string]: string[] | string | null | IMetadata;
@@ -96,7 +95,7 @@ const Metadata: React.FunctionComponent<IMetadataProps> = ({
id={`tags`}
title={`${l10n.t(LocalizationKey.panelMetadataTitle)}${contentType?.name ? ` (${contentType?.name})` : ""}`}
className={`inherit z-20`}>
<FeatureFlag features={features || DEFAULT_PANEL_FEATURE_FLAGS} flag={FEATURE_FLAG.panel.contentType}>
<FeatureFlag features={features || []} flag={FEATURE_FLAG.panel.contentType}>
<ContentTypeValidator fields={contentType?.fields || []} metadata={metadata} />
</FeatureFlag>

View File

@@ -9,6 +9,7 @@ import { TaxonomyType } from '../models';
import * as l10n from '@vscode/l10n';
import { LocalizationKey } from '../localization';
const AI_URL = WEBSITE_LINKS.api.ai;
// const AI_URL = 'http://localhost:3000/api/ai';
export class SponsorAi {
@@ -27,7 +28,7 @@ export class SponsorAi {
}, 10000);
const signal = controller.signal;
const response = await fetch(`${WEBSITE_LINKS.api.root}/ai/title`, {
const response = await fetch(`${AI_URL}/title`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -65,7 +66,7 @@ export class SponsorAi {
articleContent = articleContent.substring(0, 2000);
}
const response = await fetch(`${WEBSITE_LINKS.api.root}/ai/description`, {
const response = await fetch(`${AI_URL}/description`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -128,7 +129,7 @@ export class SponsorAi {
taxonomy: optionsString
});
const response = await fetch(`${WEBSITE_LINKS.api.root}/ai/taxonomy`, {
const response = await fetch(`${AI_URL}/taxonomy`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',