mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-03-28 17:42:40 +01:00
#705 - UX improvements for SEO panel
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#705](https://github.com/estruyf/vscode-front-matter/issues/705): UX improvements for the panel view
|
||||
- [#887](https://github.com/estruyf/vscode-front-matter/issues/887): Added new `frontMatter.global.timezone` setting, by default it is set to `UTC` for date formatting
|
||||
- [#888](https://github.com/estruyf/vscode-front-matter/issues/888): Added the ability to prompt GitHub Copilot from a custom script/action
|
||||
|
||||
|
||||
@@ -99,11 +99,6 @@
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.seo__status__details,
|
||||
.seo__status__keywords {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.collapsible__body h4 {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -258,9 +258,6 @@
|
||||
"panel.fields.textField.limit": "Feldgrenze erreicht {0}",
|
||||
"panel.fields.wrapperField.unknown": "Unbekannter Feldtyp: {0}",
|
||||
"panel.actions.title": "Aktionen",
|
||||
"panel.articleDetails.title": "Weitere Details",
|
||||
"panel.articleDetails.type": "Typ",
|
||||
"panel.articleDetails.total": "Gesamt",
|
||||
"panel.articleDetails.headings": "Überschriften",
|
||||
"panel.articleDetails.paragraphs": "Absätze",
|
||||
"panel.articleDetails.internalLinks": "Interne Links",
|
||||
@@ -299,16 +296,13 @@
|
||||
"panel.publishAction.publish": "Veröffentlichen",
|
||||
"panel.publishAction.unpublish": "Zurück zu Entwurf",
|
||||
"panel.seoDetails.recommended": "Empfohlen",
|
||||
"panel.seoKeywordInfo.density": "Stichwortdichte {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Verwendet in Überschrift(en)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Inhalt",
|
||||
"panel.seoKeywords.title": "Stichwörter",
|
||||
"panel.seoKeywords.header.keyword": "Stichwort",
|
||||
"panel.seoKeywords.header.details": "Details",
|
||||
"panel.seoKeywords.density": "* Eine Stichwortdichte von 1-1,5 % ist in den meisten Fällen ausreichend.",
|
||||
"panel.seoStatus.title": "Empfehlungen",
|
||||
"panel.seoKeywords.density.description": "* Eine Stichwortdichte von 1-1,5 % ist in den meisten Fällen ausreichend.",
|
||||
"panel.seoStatus.header.property": "Eigenschaft",
|
||||
"panel.seoStatus.header.length": "Länge",
|
||||
"panel.seoStatus.header.valid": "Gültig",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} Zeichen",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} Wörter",
|
||||
|
||||
@@ -263,9 +263,6 @@
|
||||
"panel.fields.textField.limit": "Limite de champ atteinte {0}",
|
||||
"panel.fields.wrapperField.unknown": "Type de champ inconnu : {0}",
|
||||
"panel.actions.title": "Actions",
|
||||
"panel.articleDetails.title": "Plus de détails",
|
||||
"panel.articleDetails.type": "Type",
|
||||
"panel.articleDetails.total": "Total",
|
||||
"panel.articleDetails.headings": "En-têtes",
|
||||
"panel.articleDetails.paragraphs": "Paragraphes",
|
||||
"panel.articleDetails.internalLinks": "Liens internes",
|
||||
@@ -304,16 +301,13 @@
|
||||
"panel.publishAction.publish": "Publié",
|
||||
"panel.publishAction.unpublish": "Retourner au brouillon",
|
||||
"panel.seoDetails.recommended": "Recommandé",
|
||||
"panel.seoKeywordInfo.density": "Utilisation du mot clé {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Utilisé dans le ou les en-tête(s)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Contenu",
|
||||
"panel.seoKeywords.title": "Mot-clés",
|
||||
"panel.seoKeywords.header.keyword": "Mot-clé",
|
||||
"panel.seoKeywords.header.details": "Détails",
|
||||
"panel.seoKeywords.density": "* Une densité de mot-clé de 1-1.5% est suffisante dans la plupart des cas",
|
||||
"panel.seoStatus.title": "Recommandations",
|
||||
"panel.seoKeywords.density.description": "* Une densité de mot-clé de 1-1.5% est suffisante dans la plupart des cas",
|
||||
"panel.seoStatus.header.property": "Propriété",
|
||||
"panel.seoStatus.header.length": "Longueur",
|
||||
"panel.seoStatus.header.valid": "Valide",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} caractères",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} mots",
|
||||
|
||||
@@ -263,9 +263,6 @@
|
||||
"panel.fields.textField.limit": "Limite di campi raggiunto {0}",
|
||||
"panel.fields.wrapperField.unknown": "Tipo di campo sconosciuto: {0}",
|
||||
"panel.actions.title": "Azioni",
|
||||
"panel.articleDetails.title": "Più dettagli",
|
||||
"panel.articleDetails.type": "Digitare",
|
||||
"panel.articleDetails.total": "Totale",
|
||||
"panel.articleDetails.headings": "Intestazioni",
|
||||
"panel.articleDetails.paragraphs": "Paragrafi",
|
||||
"panel.articleDetails.internalLinks": "Collegamenti esterni",
|
||||
@@ -304,16 +301,13 @@
|
||||
"panel.publishAction.publish": "Pubblica",
|
||||
"panel.publishAction.unpublish": "Tornare alla bozza",
|
||||
"panel.seoDetails.recommended": "Raccomandato",
|
||||
"panel.seoKeywordInfo.density": "Utilizzo delle parole chiave {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Utilizzato nelle rubriche",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Contenuto",
|
||||
"panel.seoKeywords.title": "Parole chiavi",
|
||||
"panel.seoKeywords.header.keyword": "Parola chiave",
|
||||
"panel.seoKeywords.header.details": "Dettagli",
|
||||
"panel.seoKeywords.density": "* Una densità di parole chiave dell'1-1,5% è sufficiente nella maggior parte dei casi.",
|
||||
"panel.seoStatus.title": "Consigli",
|
||||
"panel.seoKeywords.density.description": "* Una densità di parole chiave dell'1-1,5% è sufficiente nella maggior parte dei casi.",
|
||||
"panel.seoStatus.header.property": "Proprietà",
|
||||
"panel.seoStatus.header.length": "Lunghezza",
|
||||
"panel.seoStatus.header.valid": "Valido",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} caratteri",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} parole",
|
||||
|
||||
@@ -438,9 +438,6 @@
|
||||
|
||||
"panel.actions.title": "コマンド",
|
||||
|
||||
"panel.articleDetails.title": "詳細",
|
||||
"panel.articleDetails.type": "項目",
|
||||
"panel.articleDetails.total": "数",
|
||||
"panel.articleDetails.headings": "見出し",
|
||||
"panel.articleDetails.paragraphs": "パラグラフ",
|
||||
"panel.articleDetails.internalLinks": "内部リンク",
|
||||
@@ -489,18 +486,15 @@
|
||||
|
||||
"panel.seoDetails.recommended": "推奨",
|
||||
|
||||
"panel.seoKeywordInfo.density": "キーワード出現率 {0} *",
|
||||
"panel.seoKeywordInfo.validInfo.label": "見出しへの利用",
|
||||
"panel.seoKeywordInfo.validInfo.content": "本文",
|
||||
|
||||
"panel.seoKeywords.title": "キーワード",
|
||||
"panel.seoKeywords.header.keyword": "キーワード",
|
||||
"panel.seoKeywords.header.details": "詳細",
|
||||
"panel.seoKeywords.density": "* キーワード出現率は通常1~1.5%で十分です。",
|
||||
"panel.seoKeywords.density.description": "* キーワード出現率は通常1~1.5%で十分です。",
|
||||
|
||||
"panel.seoStatus.title": "推奨項目",
|
||||
"panel.seoStatus.header.property": "項目",
|
||||
"panel.seoStatus.header.length": "長さ",
|
||||
"panel.seoStatus.header.valid": "有効",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} 文字",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} 語",
|
||||
|
||||
@@ -448,9 +448,6 @@
|
||||
|
||||
"panel.actions.title": "Actions",
|
||||
|
||||
"panel.articleDetails.title": "More details",
|
||||
"panel.articleDetails.type": "Type",
|
||||
"panel.articleDetails.total": "Total",
|
||||
"panel.articleDetails.headings": "Headings",
|
||||
"panel.articleDetails.paragraphs": "Paragraphs",
|
||||
"panel.articleDetails.internalLinks": "Internal links",
|
||||
@@ -499,18 +496,17 @@
|
||||
|
||||
"panel.seoDetails.recommended": "Recommended",
|
||||
|
||||
"panel.seoKeywordInfo.density": "Keyword usage {0} *",
|
||||
"panel.seoKeywords.density": "Keyword density",
|
||||
"panel.seoKeywordInfo.validInfo.label": "Used in heading(s)",
|
||||
"panel.seoKeywordInfo.validInfo.content": "Content",
|
||||
|
||||
"panel.seoKeywords.title": "Keywords",
|
||||
"panel.seoKeywords.header.keyword": "Keyword",
|
||||
"panel.seoKeywords.header.details": "Details",
|
||||
"panel.seoKeywords.density": "* A keyword density of 1-1.5% is sufficient in most cases.",
|
||||
"panel.seoKeywords.density.description": "* A keyword density of 1-1.5% is sufficient in most cases.",
|
||||
|
||||
"panel.seoStatus.title": "Recommendations",
|
||||
"panel.seoStatus.title": "Insights",
|
||||
"panel.seoStatus.header.property": "Property",
|
||||
"panel.seoStatus.header.length": "Length",
|
||||
"panel.seoStatus.header.valid": "Valid",
|
||||
"panel.seoStatus.seoFieldInfo.characters": "{0} chars",
|
||||
"panel.seoStatus.seoFieldInfo.words": "{0} words",
|
||||
@@ -589,7 +585,7 @@
|
||||
"commands.i18n.createOrOpen.quickPick.category.new": "New translations",
|
||||
"commands.i18n.createOrOpen.quickPick.action.create": "Create \"{0}\"",
|
||||
"commands.i18n.translate.progress.title": "Translating content...",
|
||||
|
||||
|
||||
"commands.preview.panel.title": "Preview: {0}",
|
||||
"commands.preview.askUserToPickFolder.title": "Select the folder of the article to preview",
|
||||
|
||||
|
||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -83,6 +83,7 @@
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"recoil": "^0.7.7",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-remark": "^10.0.0",
|
||||
@@ -14702,6 +14703,20 @@
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/react-tooltip": {
|
||||
"version": "5.28.0",
|
||||
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz",
|
||||
"integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.1",
|
||||
"classnames": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.14.0",
|
||||
"react-dom": ">=16.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
||||
334
package.json
334
package.json
@@ -10,7 +10,8 @@
|
||||
"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"
|
||||
@@ -70,7 +71,8 @@
|
||||
"**/.frontmatter/config/*.json": "jsonc"
|
||||
}
|
||||
},
|
||||
"keybindings": [{
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "frontMatter.dashboard",
|
||||
"key": "alt+d"
|
||||
},
|
||||
@@ -94,19 +96,23 @@
|
||||
}
|
||||
],
|
||||
"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%",
|
||||
@@ -174,7 +180,8 @@
|
||||
"frontMatter.content.defaultFileType": {
|
||||
"type": "string",
|
||||
"default": "md",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"md",
|
||||
"mdx"
|
||||
@@ -190,7 +197,8 @@
|
||||
"frontMatter.content.defaultSorting": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"LastModifiedAsc",
|
||||
"LastModifiedDesc",
|
||||
@@ -550,7 +558,8 @@
|
||||
"categories"
|
||||
],
|
||||
"markdownDescription": "%setting.frontMatter.content.filters.markdownDescription%",
|
||||
"items": [{
|
||||
"items": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"contentFolders",
|
||||
@@ -624,7 +633,8 @@
|
||||
"command": {
|
||||
"$id": "#scriptCommand",
|
||||
"type": "string",
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"node",
|
||||
"bash",
|
||||
@@ -820,7 +830,8 @@
|
||||
"title",
|
||||
"file"
|
||||
],
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -888,7 +899,8 @@
|
||||
"id",
|
||||
"path"
|
||||
],
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"schema"
|
||||
]
|
||||
@@ -1135,26 +1147,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -1257,7 +1272,8 @@
|
||||
"fileType": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"oneOf": [{
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"md",
|
||||
"mdx"
|
||||
@@ -1403,7 +1419,8 @@
|
||||
"default": "",
|
||||
"description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%",
|
||||
"not": {
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -1609,7 +1626,8 @@
|
||||
"type",
|
||||
"name"
|
||||
],
|
||||
"allOf": [{
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@@ -1821,48 +1839,51 @@
|
||||
"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": {
|
||||
@@ -1875,7 +1896,8 @@
|
||||
"type": "string",
|
||||
"description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%",
|
||||
"not": {
|
||||
"anyOf": [{
|
||||
"anyOf": [
|
||||
{
|
||||
"const": ""
|
||||
},
|
||||
{
|
||||
@@ -2074,7 +2096,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"commands": [{
|
||||
"commands": [
|
||||
{
|
||||
"command": "frontMatter.project.switch",
|
||||
"title": "%command.frontMatter.project.switch%",
|
||||
"category": "Front Matter",
|
||||
@@ -2415,16 +2438,21 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"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 && activeEditor == 'workbench.editors.files.textFileEditor'"
|
||||
@@ -2510,11 +2538,14 @@
|
||||
"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"
|
||||
@@ -2530,7 +2561,8 @@
|
||||
"group": "frontmatter@3"
|
||||
}
|
||||
],
|
||||
"commandPalette": [{
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "frontMatter.init",
|
||||
"when": "frontMatterCanInit"
|
||||
},
|
||||
@@ -2707,7 +2739,8 @@
|
||||
"when": "frontMatter:file:isValid == true"
|
||||
}
|
||||
],
|
||||
"view/title": [{
|
||||
"view/title": [
|
||||
{
|
||||
"command": "frontMatter.docs",
|
||||
"group": "navigation@-1",
|
||||
"when": "view == frontMatter.explorer"
|
||||
@@ -2744,13 +2777,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"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": [
|
||||
@@ -2763,45 +2799,48 @@
|
||||
"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"
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"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.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": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
{
|
||||
"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:*",
|
||||
@@ -2900,6 +2939,7 @@
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"recoil": "^0.7.7",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-remark": "^10.0.0",
|
||||
@@ -2938,4 +2978,4 @@
|
||||
"vsce": {
|
||||
"dependencies": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1448,18 +1448,6 @@ export enum LocalizationKey {
|
||||
* Actions
|
||||
*/
|
||||
panelActionsTitle = 'panel.actions.title',
|
||||
/**
|
||||
* More details
|
||||
*/
|
||||
panelArticleDetailsTitle = 'panel.articleDetails.title',
|
||||
/**
|
||||
* Type
|
||||
*/
|
||||
panelArticleDetailsType = 'panel.articleDetails.type',
|
||||
/**
|
||||
* Total
|
||||
*/
|
||||
panelArticleDetailsTotal = 'panel.articleDetails.total',
|
||||
/**
|
||||
* Headings
|
||||
*/
|
||||
@@ -1613,9 +1601,9 @@ export enum LocalizationKey {
|
||||
*/
|
||||
panelSeoDetailsRecommended = 'panel.seoDetails.recommended',
|
||||
/**
|
||||
* Keyword usage {0} *
|
||||
* Keyword density
|
||||
*/
|
||||
panelSeoKeywordInfoDensity = 'panel.seoKeywordInfo.density',
|
||||
panelSeoKeywordsDensity = 'panel.seoKeywords.density',
|
||||
/**
|
||||
* Used in heading(s)
|
||||
*/
|
||||
@@ -1639,19 +1627,15 @@ export enum LocalizationKey {
|
||||
/**
|
||||
* * A keyword density of 1-1.5% is sufficient in most cases.
|
||||
*/
|
||||
panelSeoKeywordsDensity = 'panel.seoKeywords.density',
|
||||
panelSeoKeywordsDensityDescription = 'panel.seoKeywords.density.description',
|
||||
/**
|
||||
* Recommendations
|
||||
* Insights
|
||||
*/
|
||||
panelSeoStatusTitle = 'panel.seoStatus.title',
|
||||
/**
|
||||
* Property
|
||||
*/
|
||||
panelSeoStatusHeaderProperty = 'panel.seoStatus.header.property',
|
||||
/**
|
||||
* Length
|
||||
*/
|
||||
panelSeoStatusHeaderLength = 'panel.seoStatus.header.length',
|
||||
/**
|
||||
* Valid
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { VSCodeTable, VSCodeTableBody, VSCodeTableCell, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
import { VSCodeTableCell, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
|
||||
export interface IArticleDetailsProps {
|
||||
details: {
|
||||
@@ -22,59 +22,42 @@ const ArticleDetails: React.FunctionComponent<IArticleDetailsProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`seo__status__details valid`}>
|
||||
<h4>{l10n.t(LocalizationKey.panelArticleDetailsTitle)}</h4>
|
||||
<>
|
||||
{details?.headings !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsHeadings)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.headings}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
|
||||
<VSCodeTable>
|
||||
<VSCodeTableHeader>
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableHead>
|
||||
{l10n.t(LocalizationKey.panelArticleDetailsType)}
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead>
|
||||
{l10n.t(LocalizationKey.panelArticleDetailsTotal)}
|
||||
</VSCodeTableHead>
|
||||
</VSCodeTableRow>
|
||||
</VSCodeTableHeader>
|
||||
{details?.paragraphs !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsParagraphs)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.paragraphs}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
|
||||
<VSCodeTableBody>
|
||||
{details?.headings !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsHeadings)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.headings}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
{details?.internalLinks !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsInternalLinks)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.internalLinks}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
|
||||
{details?.paragraphs !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsParagraphs)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.paragraphs}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
{details?.externalLinks !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsExternalLinks)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.externalLinks}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
|
||||
{details?.internalLinks !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsInternalLinks)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.internalLinks}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
|
||||
{details?.externalLinks !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsExternalLinks)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.externalLinks}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
|
||||
{details?.images !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsImages)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.images}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
</VSCodeTableBody>
|
||||
</VSCodeTable>
|
||||
</div>
|
||||
{details?.images !== undefined && (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{l10n.t(LocalizationKey.panelArticleDetailsImages)}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{details.images}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,19 +7,20 @@ export interface ISeoFieldInfoProps {
|
||||
value: string;
|
||||
recommendation: string;
|
||||
isValid?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SeoFieldInfo: React.FunctionComponent<ISeoFieldInfoProps> = ({
|
||||
title,
|
||||
value,
|
||||
recommendation,
|
||||
isValid
|
||||
isValid,
|
||||
className
|
||||
}: React.PropsWithChildren<ISeoFieldInfoProps>) => {
|
||||
return (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableRow className={className || ""}>
|
||||
<VSCodeTableCell className={`capitalize`}>{title}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{value}/{recommendation}</VSCodeTableCell>
|
||||
<VSCodeTableCell>{isValid !== undefined ? <ValidInfo label={undefined} isValid={isValid} /> : <span>-</span>}</VSCodeTableCell>
|
||||
<VSCodeTableCell className='flex items-center text-nowrap'>{isValid !== undefined ? <ValidInfo label={undefined} isValid={isValid} /> : <span className='inline-block w-4 mr-2'>—</span>} {value}/{recommendation}</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { ValidInfo } from './ValidInfo';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { VSCodeTableCell, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
|
||||
export interface ISeoKeywordInfoProps {
|
||||
@@ -31,14 +29,14 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
const pattern = new RegExp(`(^${keyword.toLowerCase()}(?=\\s|$))|(\\s${keyword.toLowerCase()}(?=\\s|$))`, 'ig');
|
||||
const count = (content.match(pattern) || []).length;
|
||||
const density = (count / wordCount) * 100;
|
||||
const densityTitle = l10n.t(LocalizationKey.panelSeoKeywordInfoDensity, `${density.toFixed(2)}%`);
|
||||
const densityTitle = `${density.toFixed(2)}% *`;
|
||||
|
||||
if (density < 0.75) {
|
||||
return <ValidInfo label={densityTitle} isValid={false} />;
|
||||
return <ValidInfo label={densityTitle} isValid={false} className='text-xs' />;
|
||||
} else if (density >= 0.75 && density < 1.5) {
|
||||
return <ValidInfo label={densityTitle} isValid={true} />;
|
||||
return <ValidInfo label={densityTitle} isValid={true} className='text-xs' />;
|
||||
} else {
|
||||
return <ValidInfo label={densityTitle} isValid={false} />;
|
||||
return <ValidInfo label={densityTitle} isValid={false} className='text-xs' />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -63,7 +61,7 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
}
|
||||
|
||||
const exists = headings.filter((heading) => validateKeywords(heading, keyword));
|
||||
return <ValidInfo label={l10n.t(LocalizationKey.panelSeoKeywordInfoValidInfoLabel)} isValid={exists.length > 0} />;
|
||||
return <ValidInfo isValid={exists.length > 0} />;
|
||||
};
|
||||
|
||||
if (!keyword || typeof keyword !== 'string') {
|
||||
@@ -73,39 +71,35 @@ const SeoKeywordInfo: React.FunctionComponent<ISeoKeywordInfoProps> = ({
|
||||
return (
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableCell>{keyword}</VSCodeTableCell>
|
||||
<VSCodeTableCell className={` table__cell__validation`}>
|
||||
<div className='flex items-center'>
|
||||
<ValidInfo
|
||||
label={l10n.t(LocalizationKey.commonTitle)}
|
||||
isValid={!!title && title.toLowerCase().includes(keyword.toLowerCase())}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<ValidInfo
|
||||
label={l10n.t(LocalizationKey.commonDescription)}
|
||||
isValid={!!description && description.toLowerCase().includes(keyword.toLowerCase())}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<ValidInfo
|
||||
label={l10n.t(LocalizationKey.commonSlug)}
|
||||
isValid={
|
||||
!!slug &&
|
||||
(slug.toLowerCase().includes(keyword.toLowerCase()) ||
|
||||
slug.toLowerCase().includes(keyword.replace(/ /g, '-').toLowerCase()))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<ValidInfo
|
||||
label={l10n.t(LocalizationKey.panelSeoKeywordInfoValidInfoContent)}
|
||||
isValid={!!content && content.toLowerCase().includes(keyword.toLowerCase())}
|
||||
/>
|
||||
</div>
|
||||
{headings && headings.length > 0 &&
|
||||
<div className='flex items-center'>{checkHeadings()}</div>}
|
||||
{wordCount &&
|
||||
<div className='flex items-center'>{density()}</div>}
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
<ValidInfo
|
||||
isValid={!!title && title.toLowerCase().includes(keyword.toLowerCase())}
|
||||
/>
|
||||
</VSCodeTableCell>
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
<ValidInfo
|
||||
isValid={!!description && description.toLowerCase().includes(keyword.toLowerCase())}
|
||||
/>
|
||||
</VSCodeTableCell>
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
<ValidInfo
|
||||
isValid={
|
||||
!!slug &&
|
||||
(slug.toLowerCase().includes(keyword.toLowerCase()) ||
|
||||
slug.toLowerCase().includes(keyword.replace(/ /g, '-').toLowerCase()))
|
||||
}
|
||||
/>
|
||||
</VSCodeTableCell>
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
<ValidInfo
|
||||
isValid={!!content && content.toLowerCase().includes(keyword.toLowerCase())}
|
||||
/>
|
||||
</VSCodeTableCell>
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
{checkHeadings()}
|
||||
</VSCodeTableCell>
|
||||
<VSCodeTableCell className={`text-center`}>
|
||||
{density()}
|
||||
</VSCodeTableCell>
|
||||
</VSCodeTableRow>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { SeoKeywordInfo } from './SeoKeywordInfo';
|
||||
import { ErrorBoundary } from '@sentry/react';
|
||||
import * as l10n from '@vscode/l10n';
|
||||
import { LocalizationKey } from '../../localization';
|
||||
import { Tooltip } from 'react-tooltip'
|
||||
import { LocalizationKey, localize } from '../../localization';
|
||||
import { VSCodeTable, VSCodeTableBody, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
import { Icon } from 'vscrui';
|
||||
|
||||
export interface ISeoKeywordsProps {
|
||||
keywords: string[] | null;
|
||||
@@ -22,6 +23,8 @@ const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
}: React.PropsWithChildren<ISeoKeywordsProps>) => {
|
||||
const [isReady, setIsReady] = React.useState(false);
|
||||
|
||||
const tooltipClasses = `!py-[2px] !px-[8px] !rounded-[3px] !border-[var(--vscode-editorHoverWidget-border)] !border !border-solid !bg-[var(--vscode-editorHoverWidget-background)] !text-[var(--vscode-editorHoverWidget-foreground)] !font-normal !opacity-100`;
|
||||
|
||||
const validateKeywords = () => {
|
||||
if (!keywords) {
|
||||
return [];
|
||||
@@ -51,17 +54,106 @@ const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`seo__status__keywords`}>
|
||||
<h4>{l10n.t(LocalizationKey.panelSeoKeywordsTitle)}</h4>
|
||||
<section className={`seo__keywords mb-8`}>
|
||||
<h4 className='!text-left'>{localize(LocalizationKey.panelSeoKeywordsTitle)}</h4>
|
||||
|
||||
<VSCodeTable>
|
||||
<VSCodeTableHeader>
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableRow className={`border-t border-t-[var(--vscode-editorGroup-border)]`}>
|
||||
<VSCodeTableHead>
|
||||
{l10n.t(LocalizationKey.panelSeoKeywordsHeaderKeyword)}
|
||||
{localize(LocalizationKey.panelSeoKeywordsHeaderKeyword)}
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead>
|
||||
{l10n.t(LocalizationKey.panelSeoKeywordsHeaderDetails)}
|
||||
<VSCodeTableHead className='text-center'>
|
||||
<div
|
||||
className='flex items-center justify-center h-full'
|
||||
>
|
||||
<Icon
|
||||
className='!text-[var(--vscode-foreground)]'
|
||||
name='quote'
|
||||
data-tooltip-id="tooltip-title"
|
||||
data-tooltip-content={localize(LocalizationKey.commonTitle)} />
|
||||
<Tooltip id="tooltip-title" className={tooltipClasses} style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}} />
|
||||
</div>
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead className='text-center'>
|
||||
<div
|
||||
className='flex items-center justify-center h-full'
|
||||
>
|
||||
<Icon
|
||||
className='!text-[var(--vscode-foreground)]'
|
||||
name='note'
|
||||
data-tooltip-id="tooltip-description"
|
||||
data-tooltip-content={localize(LocalizationKey.commonDescription)} />
|
||||
<Tooltip id="tooltip-description" className={tooltipClasses} style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}} />
|
||||
</div>
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead className='text-center'>
|
||||
<div
|
||||
className='flex items-center justify-center h-full'
|
||||
>
|
||||
<Icon
|
||||
className='!text-[var(--vscode-foreground)]'
|
||||
name='link'
|
||||
data-tooltip-id="tooltip-slug"
|
||||
data-tooltip-content={localize(LocalizationKey.commonSlug)} />
|
||||
<Tooltip id="tooltip-slug" className={tooltipClasses} style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}} />
|
||||
</div>
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead className='text-center'>
|
||||
<div
|
||||
className='flex items-center justify-center h-full'
|
||||
>
|
||||
<Icon
|
||||
className='!text-[var(--vscode-foreground)]'
|
||||
name='book'
|
||||
data-tooltip-id="tooltip-content"
|
||||
data-tooltip-content={localize(LocalizationKey.panelSeoKeywordInfoValidInfoContent)} />
|
||||
<Tooltip id="tooltip-content" className={tooltipClasses} style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}} />
|
||||
</div>
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead className='text-center'>
|
||||
<div
|
||||
className='flex items-center justify-center h-full'
|
||||
>
|
||||
<span
|
||||
className='text-[var(--vscode-foreground)] cursor-default select-none'
|
||||
data-tooltip-id="tooltip-heading"
|
||||
data-tooltip-content={localize(LocalizationKey.panelSeoKeywordInfoValidInfoLabel)}
|
||||
>
|
||||
H1
|
||||
</span>
|
||||
<Tooltip id="tooltip-heading" className={tooltipClasses} style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}} />
|
||||
</div>
|
||||
</VSCodeTableHead>
|
||||
<VSCodeTableHead className='text-center'>
|
||||
<div
|
||||
className='flex items-center justify-center h-full'
|
||||
>
|
||||
<Icon
|
||||
className='!text-[var(--vscode-foreground)]'
|
||||
name='percentage'
|
||||
data-tooltip-id="tooltip-density"
|
||||
data-tooltip-content={localize(LocalizationKey.panelSeoKeywordsDensity)} />
|
||||
<Tooltip id="tooltip-density" className={tooltipClasses} style={{
|
||||
fontSize: '12px',
|
||||
lineHeight: '19px'
|
||||
}} />
|
||||
</div>
|
||||
</VSCodeTableHead>
|
||||
</VSCodeTableRow>
|
||||
</VSCodeTableHeader>
|
||||
@@ -79,10 +171,10 @@ const SeoKeywords: React.FunctionComponent<ISeoKeywordsProps> = ({
|
||||
|
||||
{data.wordCount && (
|
||||
<div className={`text-xs mt-2`}>
|
||||
{l10n.t(LocalizationKey.panelSeoKeywordsDensity)}
|
||||
{localize(LocalizationKey.panelSeoKeywordsDensityDescription)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import { TagType } from '../TagType';
|
||||
import { ArticleDetails } from './ArticleDetails';
|
||||
import { Collapsible } from './Collapsible';
|
||||
import FieldBoundary from './ErrorBoundary/FieldBoundary';
|
||||
import { SymbolKeywordIcon } from './Icons/SymbolKeywordIcon';
|
||||
import { SeoFieldInfo } from './SeoFieldInfo';
|
||||
import { SeoKeywords } from './SeoKeywords';
|
||||
import { TagPicker } from './Fields/TagPicker';
|
||||
import { LocalizationKey, localize } from '../../localization';
|
||||
import { VSCodeTable, VSCodeTableBody, VSCodeTableHead, VSCodeTableHeader, VSCodeTableRow } from './VSCode/VSCodeTable';
|
||||
import { VSCodeTable, VSCodeTableBody } from './VSCode/VSCodeTable';
|
||||
import useContentType from '../../hooks/useContentType';
|
||||
import { Icon } from 'vscrui';
|
||||
|
||||
export interface ISeoStatusProps {
|
||||
seo: SEO;
|
||||
@@ -38,19 +38,11 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
const descriptionFieldName = contentType?.fields.find(f => f.name === descriptionField)?.title || descriptionField;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={`seo__status__details`}>
|
||||
<h4>{localize(LocalizationKey.panelSeoStatusTitle)}</h4>
|
||||
<div className='space-y-8'>
|
||||
<section className={`seo__insights`}>
|
||||
<h4 className='!text-left'>{localize(LocalizationKey.panelSeoStatusTitle)}</h4>
|
||||
|
||||
<VSCodeTable>
|
||||
<VSCodeTableHeader>
|
||||
<VSCodeTableRow>
|
||||
<VSCodeTableHead>{localize(LocalizationKey.panelSeoStatusHeaderProperty)}</VSCodeTableHead>
|
||||
<VSCodeTableHead>{localize(LocalizationKey.panelSeoStatusHeaderLength)}</VSCodeTableHead>
|
||||
<VSCodeTableHead>{localize(LocalizationKey.panelSeoStatusHeaderValid)}</VSCodeTableHead>
|
||||
</VSCodeTableRow>
|
||||
</VSCodeTableHeader>
|
||||
|
||||
<VSCodeTableBody>
|
||||
{metadata[titleField] && seo.title > 0 ? (
|
||||
<SeoFieldInfo
|
||||
@@ -58,6 +50,7 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
value={metadata[titleField].length}
|
||||
recommendation={localize(LocalizationKey.panelSeoStatusSeoFieldInfoCharacters, seo.title)}
|
||||
isValid={metadata[titleField].length <= seo.title}
|
||||
className={`border-t border-t-[var(--vscode-editorGroup-border)]`}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -86,9 +79,11 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
recommendation={localize(LocalizationKey.panelSeoStatusSeoFieldInfoWords, seo.content)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<ArticleDetails details={metadata.articleDetails} />
|
||||
</VSCodeTableBody>
|
||||
</VSCodeTable>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<SeoKeywords
|
||||
keywords={metadata?.keywords}
|
||||
@@ -103,7 +98,7 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
<FieldBoundary fieldName={`Keywords`}>
|
||||
<TagPicker
|
||||
type={TagType.keywords}
|
||||
icon={<SymbolKeywordIcon />}
|
||||
icon={<Icon name="symbol-keyword" className='mr-2' />}
|
||||
crntSelected={(metadata.keywords as string[]) || []}
|
||||
options={[]}
|
||||
freeform={true}
|
||||
@@ -112,8 +107,6 @@ const SeoStatus: React.FunctionComponent<ISeoStatusProps> = ({
|
||||
disableConfigurable
|
||||
/>
|
||||
</FieldBoundary>
|
||||
|
||||
<ArticleDetails details={metadata.articleDetails} />
|
||||
</div>
|
||||
);
|
||||
}, [contentType, metadata, seo, focusElm, unsetFocus]);
|
||||
|
||||
@@ -4,21 +4,23 @@ import { CheckIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'
|
||||
export interface IValidInfoProps {
|
||||
label?: string;
|
||||
isValid: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ValidInfo: React.FunctionComponent<IValidInfoProps> = ({
|
||||
label,
|
||||
isValid
|
||||
isValid,
|
||||
className,
|
||||
}: React.PropsWithChildren<IValidInfoProps>) => {
|
||||
return (
|
||||
<>
|
||||
<div className='inline-flex items-center h-full'>
|
||||
{isValid ? (
|
||||
<CheckIcon className={`h-4 w-4 text-[#46ec86] mr-2`} />
|
||||
) : (
|
||||
<ExclamationTriangleIcon className={`h-4 w-4 text-[var(--vscode-statusBarItem-warningBackground)] mr-2`} />
|
||||
)}
|
||||
{label && <span>{label}</span>}
|
||||
</>
|
||||
{label && <span className={className || ""}>{label}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user