diff --git a/assets/media/styles.css b/assets/media/styles.css index 4b0923df..e8fb6645 100644 --- a/assets/media/styles.css +++ b/assets/media/styles.css @@ -93,6 +93,11 @@ margin-bottom: 2rem; } +.seo__status__details h4 { + text-align: center; + font-weight: bold; +} + .not-valid { color: var(--vscode-errorForeground); } @@ -244,13 +249,28 @@ margin-right: .5rem; } +.ext_link_block button, .ext_link_block a { - color: var(--vscode-textLink-foreground); + align-items: center; + color: var(--vscode-button-secondaryForeground); + background-color: var(--vscode-button-secondaryBackground); + border: 0px; + border-radius: 0px; + box-sizing: border-box; + cursor: pointer; + display: inline-flex; + font-size: var(--vscode-font-size); + font-weight: var(--vscode-font-weight); + line-height: 26px; + padding: 0px 14px; + user-select: none; + text-decoration: none; + width: auto; } .ext_link_block a:hover, .ext_link_block a:active, .ext_link_block a:focus, .ext_link_block a:visited { - color: var(--vscode-textLink-activeForeground); + color: var(--vscode-button-secondaryForeground); } \ No newline at end of file diff --git a/package.json b/package.json index 1488b0b6..5ab497bc 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,11 @@ "default": 160, "description": "Specifies the optimal description length for SEO (set to `-1` to turn it off)." }, + "frontMatter.taxonomy.seoContentLengh": { + "type": "number", + "default": 1760, + "description": "Specifies the optimal minimum length for your articles. Between 1,760 words – 2,400 is the absolute ideal blog length for SEO in 2021. (set to `-1` to turn it off)." + }, "frontMatter.taxonomy.seoDescriptionField": { "type": "string", "default": "description", diff --git a/src/constants/settings.ts b/src/constants/settings.ts index 2a184ba3..befd3921 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -18,6 +18,7 @@ export const SETTING_FRONTMATTER_TYPE = "taxonomy.frontMatterType"; export const SETTING_SEO_TITLE_LENGTH = "taxonomy.seoTitleLength"; export const SETTING_SEO_DESCRIPTION_LENGTH = "taxonomy.seoDescriptionLength"; +export const SETTING_SEO_CONTENT_MIN_LENGTH = "taxonomy.seoContentLengh"; export const SETTING_SEO_DESCRIPTION_FIELD = "taxonomy.seoDescriptionField"; export const SETTING_TEMPLATES_FOLDER = "templates.folder"; diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 4ad2ce25..d15018f6 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -11,6 +11,7 @@ export interface PanelSettings { export interface SEO { title: number; description: number; + content: number; descriptionField: string; } diff --git a/src/viewpanel/ViewPanel.tsx b/src/viewpanel/ViewPanel.tsx index 8bdc5cfb..715eebbc 100644 --- a/src/viewpanel/ViewPanel.tsx +++ b/src/viewpanel/ViewPanel.tsx @@ -77,23 +77,19 @@ export const ViewPanel: React.FunctionComponent = (props: React
- - Open settings +
- - Reveal file in folder +
- - Reveal project folder +
- - Report an issue + Report an issue
diff --git a/src/viewpanel/components/ArticleDetails.tsx b/src/viewpanel/components/ArticleDetails.tsx new file mode 100644 index 00000000..9f8c0a67 --- /dev/null +++ b/src/viewpanel/components/ArticleDetails.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell, VsTableRow, VsTableCell } from './VscodeComponents'; + +export interface IArticleDetailsProps { + details: { + headings: number; + paragraphs: number; + wordCount: number; + } +} + +export const ArticleDetails: React.FunctionComponent = ({details}: React.PropsWithChildren) => { + + console.log(details); + + if (!details || (details.headings === undefined && details.paragraphs === undefined)) { + return null; + } + + return ( +
+

More details

+ + + + Type + Total + + + { + details?.headings !== undefined && ( + + Headings + {details.headings} + + ) + } + + { + details?.paragraphs !== undefined && ( + + Paragraphs + {details.paragraphs} + + ) + } + + +
+ ); +}; \ No newline at end of file diff --git a/src/viewpanel/components/SeoDetails.tsx b/src/viewpanel/components/SeoDetails.tsx index 7fe43fc4..6d188eb3 100644 --- a/src/viewpanel/components/SeoDetails.tsx +++ b/src/viewpanel/components/SeoDetails.tsx @@ -1,17 +1,19 @@ import * as React from 'react'; +import { VsTable, VsTableBody, VsTableHeader, VsTableHeaderCell, VsTableRow, VsTableCell } from './VscodeComponents'; export interface ISeoDetailsProps { - allowedLength?: number; + allowedLength: number; title: string; value: number; valueTitle: string; + noValidation?: boolean; } export const SeoDetails: React.FunctionComponent = (props: React.PropsWithChildren) => { - const { allowedLength, title, value, valueTitle } = props; + const { allowedLength, title, value, valueTitle, noValidation } = props; const validate = () => { - if (allowedLength === undefined) { + if (noValidation) { return ""; } @@ -19,12 +21,21 @@ export const SeoDetails: React.FunctionComponent = (props: Rea }; return ( -
-

{title}

-
    -
  • {valueTitle}: {value}
  • - { allowedLength &&
  • Recommended length: {allowedLength}
  • } -
+
+

{title}

+ + + + {valueTitle} + Recommended + + + + {value} + {allowedLength} + + +
); }; \ No newline at end of file diff --git a/src/viewpanel/components/SeoStatus.tsx b/src/viewpanel/components/SeoStatus.tsx index f9a16bc6..7c587b5e 100644 --- a/src/viewpanel/components/SeoStatus.tsx +++ b/src/viewpanel/components/SeoStatus.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { SEO } from '../../models/PanelSettings'; +import { ArticleDetails } from './ArticleDetails'; import { Icon } from './Icon'; import { SeoDetails } from './SeoDetails'; @@ -28,16 +29,17 @@ export const SeoStatus: React.FunctionComponent = (props: React { (data[descriptionField] && seo.description > 0) && } - { data?.articleDetails !== null && ( -
-

Article details

-
    - { data?.articleDetails?.headings &&
  • Headings: {data?.articleDetails?.headings}
  • } - { data?.articleDetails?.paragraphs &&
  • Paragraphs: {data?.articleDetails?.paragraphs}
  • } - { data?.articleDetails?.wordCount &&
  • Words: {data?.articleDetails?.wordCount}
  • } -
-
- ) } + { + seo.content > 0 && data?.articleDetails?.wordCount && ( + + ) + } + +
); }; \ No newline at end of file diff --git a/src/viewpanel/components/VscodeComponents.ts b/src/viewpanel/components/VscodeComponents.ts index 81a3b561..65df271e 100644 --- a/src/viewpanel/components/VscodeComponents.ts +++ b/src/viewpanel/components/VscodeComponents.ts @@ -1,3 +1,8 @@ import {wrapWc} from 'wc-react'; -export const VscodeIcon = wrapWc(`vscode-icon`); \ No newline at end of file +export const VsTable = wrapWc(`vscode-table`); +export const VsTableHeader = wrapWc(`vscode-table-header`); +export const VsTableHeaderCell = wrapWc(`vscode-table-header-cell`); +export const VsTableBody = wrapWc(`vscode-table-body`); +export const VsTableRow = wrapWc(`vscode-table-row`); +export const VsTableCell = wrapWc(`vscode-table-cell`); \ No newline at end of file diff --git a/src/viewpanel/index.tsx b/src/viewpanel/index.tsx index 4d5d7504..336825bd 100644 --- a/src/viewpanel/index.tsx +++ b/src/viewpanel/index.tsx @@ -2,7 +2,12 @@ import * as React from "react"; import { render } from "react-dom"; import { ViewPanel } from "./ViewPanel"; -import '@bendera/vscode-webview-elements/dist/vscode-icon'; +import '@bendera/vscode-webview-elements/dist/vscode-table'; +import '@bendera/vscode-webview-elements/dist/vscode-table-header'; +import '@bendera/vscode-webview-elements/dist/vscode-table-header-cell'; +import '@bendera/vscode-webview-elements/dist/vscode-table-body'; +import '@bendera/vscode-webview-elements/dist/vscode-table-row'; +import '@bendera/vscode-webview-elements/dist/vscode-table-cell'; declare const acquireVsCodeApi: () => { getState: () => T; diff --git a/src/webview/ExplorerView.ts b/src/webview/ExplorerView.ts index 94143470..75c7ca34 100644 --- a/src/webview/ExplorerView.ts +++ b/src/webview/ExplorerView.ts @@ -1,4 +1,4 @@ -import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_DESCRIPTION_FIELD } from './../constants/settings'; +import { SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD } from './../constants/settings'; import * as os from 'os'; import { PanelSettings, CustomScript } from './../models/PanelSettings'; import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode"; @@ -224,6 +224,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { seo: { title: config.get(SETTING_SEO_TITLE_LENGTH) as number || -1, description: config.get(SETTING_SEO_DESCRIPTION_LENGTH) as number || -1, + content: config.get(SETTING_SEO_CONTENT_MIN_LENGTH) as number || -1, descriptionField: config.get(SETTING_SEO_DESCRIPTION_FIELD) as string || "description" }, slug: {