diff --git a/.templates/documentation.md b/.templates/documentation.md new file mode 100644 index 00000000..57201c1c --- /dev/null +++ b/.templates/documentation.md @@ -0,0 +1,8 @@ +--- +title: "{{name}}" +slug: "/{{kebabCase name}}/" +description: +date: 2019-08-22T15:20:28.000Z +lastmod: 2019-08-22T15:20:28.000Z +weight: 1 +--- diff --git a/.vscode/settings.json b/.vscode/settings.json index 30bf8c2d..7cef107f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,17 @@ "out": true // set this to false to include "out" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off" + "typescript.tsc.autoDetect": "off", + "frontMatter.content.folders": [ + { + "title": "documentation", + "fsPath": "/Users/eliostruyf/nodejs/vscode/vscode-front-matter/docs/content/docs", + "paths": [ + "/Users/eliostruyf/nodejs/vscode/vscode-front-matter/docs/content/docs", + "\\Users\\eliostruyf\\nodejs\\vscode\\vscode-front-matter\\docs\\content\\docs" + ] + } + ], + "eliostruyf.writingstyleguide.terms.isDisabled": true, + "eliostruyf.writingstyleguide.biasFree.isDisabled": true } \ No newline at end of file diff --git a/docs/components/Docs/Markdown.tsx b/docs/components/Docs/Markdown.tsx new file mode 100644 index 00000000..6bb7a2f7 --- /dev/null +++ b/docs/components/Docs/Markdown.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import ReactMarkdown from 'react-markdown'; +import rehypeRaw from 'rehype-raw'; + +export interface IMarkdownProps { + content: string | undefined; +} + +export const Markdown: React.FunctionComponent = ({content}: React.PropsWithChildren) => { + if (!content) { + return null; + } + + const getTitle = (props: any) => { + const title = props?.children.length > 0 ? `${props?.children[0] as string}` : ""; + return title; + }; + + const generateId = (props: any) => { + const title = getTitle(props); + return title.toLowerCase().replace(/\s/g, '-'); + }; + + return ( +
+ {/* eslint-disable react/no-children-prop */} + { + const url = props?.href || ""; + const title = getTitle(props); + const elm = {title}; + return elm; + }, + h1: ({node, ...props}) => (

{getTitle(props)}

), + h2: ({node, ...props}) => (

{getTitle(props)}

), + h3: ({node, ...props}) => (

{getTitle(props)}

), + }} + rehypePlugins={[rehypeRaw]} + children={content} /> +
+ ); +}; \ No newline at end of file diff --git a/docs/components/Docs/Page.tsx b/docs/components/Docs/Page.tsx new file mode 100644 index 00000000..9bbe4ded --- /dev/null +++ b/docs/components/Docs/Page.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { PageFrontMatter } from '../../models/PageFrontMatter'; +import { Sidebar } from './Sidebar'; + +export interface IPageProps { + items: PageFrontMatter[]; +} + +export const Page: React.FunctionComponent = ({items, children}: React.PropsWithChildren) => { + return ( +
+
+ + + +
+ {children} +
+
+
+ ); +}; + diff --git a/docs/components/Docs/Sidebar.tsx b/docs/components/Docs/Sidebar.tsx new file mode 100644 index 00000000..2881ad72 --- /dev/null +++ b/docs/components/Docs/Sidebar.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { PageFrontMatter } from '../../models/PageFrontMatter'; +import { Link } from '../Link/Link'; +import { Section } from '../Link/Section'; + +export interface ISidebarProps { + items: PageFrontMatter[]; +} + +export const Sidebar: React.FunctionComponent = ({ items }: React.PropsWithChildren) => { + + const sorted = items?.sort((a, b) => { return (a.weight || 99) - (b.weight || 99); }) || []; + + const getLinks = (item: PageFrontMatter) => { + const { content } = item; + const links = Array.from(content.matchAll(/^## (.*$)/gim)); + + if (!links || links.length === 0) { + return null; + } + + return ( +
    + {links.map((link, index) => ( +
  • + +
  • + ))} +
+ ); + } + + return ( + + ); +}; \ No newline at end of file diff --git a/docs/components/Link/Link.tsx b/docs/components/Link/Link.tsx index a6dbb456..a2c093c1 100644 --- a/docs/components/Link/Link.tsx +++ b/docs/components/Link/Link.tsx @@ -1,11 +1,15 @@ import * as React from 'react'; -export interface ILinkProps {} +export interface ILinkProps { + title: string; + link: string; +} -export const Link: React.FunctionComponent = (props: React.PropsWithChildren) => { +export const Link: React.FunctionComponent = ({title, link}: React.PropsWithChildren) => { return ( - <> - - + + {title} + ); }; \ No newline at end of file diff --git a/docs/components/Link/Section.tsx b/docs/components/Link/Section.tsx new file mode 100644 index 00000000..0528d11b --- /dev/null +++ b/docs/components/Link/Section.tsx @@ -0,0 +1,18 @@ +import Link from 'next/link'; +import * as React from 'react'; + +export interface ISectionProps { + title: string; + link: string; +} + +export const Section: React.FunctionComponent = ({title, link}: React.PropsWithChildren) => { + + return ( + + + {title} + + + ); +}; \ No newline at end of file diff --git a/docs/content/docs/commands.md b/docs/content/docs/commands.md new file mode 100644 index 00000000..5f411a20 --- /dev/null +++ b/docs/content/docs/commands.md @@ -0,0 +1,8 @@ +--- +title: Commands +slug: commands +description: +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 6 +--- \ No newline at end of file diff --git a/docs/content/docs/custom-actions.md b/docs/content/docs/custom-actions.md new file mode 100644 index 00000000..4d6a4742 --- /dev/null +++ b/docs/content/docs/custom-actions.md @@ -0,0 +1,8 @@ +--- +title: Custom actions +slug: custom-actions +description: +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 5 +--- \ No newline at end of file diff --git a/docs/content/docs/dashboard.md b/docs/content/docs/dashboard.md new file mode 100644 index 00000000..ff190c8c --- /dev/null +++ b/docs/content/docs/dashboard.md @@ -0,0 +1,8 @@ +--- +title: Dashboard +slug: dashboard +description: +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 3 +--- \ No newline at end of file diff --git a/docs/content/docs/getting-started.md b/docs/content/docs/getting-started.md new file mode 100644 index 00000000..28926afe --- /dev/null +++ b/docs/content/docs/getting-started.md @@ -0,0 +1,49 @@ +--- +title: Getting started +slug: getting-started +description: +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 2 +--- + +# Getting started + +To get you started, you first need to install the extension in Visual Studio Code. + +You can get the extension via: + +- The VS Code marketplace: [VS Code Marketplace - Front Matter](https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter). +- The extension CLI: `ext install eliostruyf.vscode-front-matter` + +## Welcome screen + +Once installed, Front Matter will open the **welcome screen** the first time Visual Studio Code gets reloaded. + +![Welcome screen](/assets/welcome-progress.png) + +It also supports light themes: + +![Welcome screen](/assets/welcome-light.png) + +> **Info**: The welcome screen will also be shown when Front Matter is not yet fully configured. + +## Required configuration + +On the welcome screen, there are two tasks to complete before you can take full advantage of Front Matter. + +### Step 1: Initialize the project + +In this step, a `.templates` folder and `article.md` file template will be created in the current project. + +The `.templates` folder, is a folder that can be used to place all sort of Markdown templates. It will be used when you want to let Front Matter generate new pages/articles/... + +### Step 2: Register content folder(s) + +As Front Matter is **not** created to only support one static site generator, you will be able to specify where your Markdown content lives. From the moment you register a folder, it will be used on the dashboard to show an overview of all files. + +You can register a folder by right-clicking on a folder name in the explorer panel from Visual Studio Code and selecting **Front Matter: Register folder**. + +![Register a folder](/assets/register-folder.png) + +## Enjoy using Front Matter \ No newline at end of file diff --git a/docs/content/docs/index.md b/docs/content/docs/index.md new file mode 100644 index 00000000..126702b9 --- /dev/null +++ b/docs/content/docs/index.md @@ -0,0 +1,52 @@ +--- +title: Introduction +slug: '' +description: Create, edit, and preview your pages within Visual Studio Code. Front Matter allows you to keep control of your static site without any external tools. +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 1 +--- + +# Introduction + +Front Matter is an essential Visual Studio Code extension that simplifies working and managing your markdown articles. We created the extension to support many static-site generators like Hugo, Jekyll, Hexo, NextJs, Gatsby, and more. + +The extension brings Content Management System (CMS) capabilities straight within Visual Studio Code. For example, you can keep a list of the used tags, categories, create content, and so much more. + +![Welcome screen](/assets/welcome-progress.png) + +Our main extension features are: + +- Page dashboard where you can get an overview of all your markdown pages. You can use it to search, filter, sort your contents. +- Site preview within Visual Studio Code +- SEO checks for title, description, and keywords +- Support for custom actions/scripts +- and many more + +![Site preview](/assets/site-preview.png) + +## Why Front Matter? + +Initially, the Front Matter extension was created when [Elio Struyf](https://twitter.com/eliostruyf) migrated from WordPress to Hugo (Static Site Generator). To make content management more straightforward, he started to develop the Front Matter extension. He added more features regularly, and eventually, it became a headless CMS that runs within Visual Studio Code. + +## Advantages + +We believe that Front Matter gives you the following advantages: + +### Speed + +It just runs on your machine. There are no servers/websites/APIs involved in the process. Nothing can beat this. + +### Use it within Visual Studio Code + +You do not need to jump from tool to tool. Just use the one that you like the most, which is, of course, Visual Studio Code. + +### Customizable + +Almost all of the Front Matter features are customizable by the extension of its settings. These settings make sure that you can tweak it to your needs. + +### Extensibility + +We know that not every site is the same. That is why we allow you to add your custom scripts. These scripts will show up as actions in our panel and could take your content management to the next level. + +> **Example**: [Generate open graph preview image in Code with Front Matter](https://www.eliostruyf.com/generate-open-graph-preview-image-code-front-matter/) \ No newline at end of file diff --git a/docs/content/docs/panel.md b/docs/content/docs/panel.md new file mode 100644 index 00000000..2b9dee4b --- /dev/null +++ b/docs/content/docs/panel.md @@ -0,0 +1,8 @@ +--- +title: Panel +slug: panel +description: +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 4 +--- \ No newline at end of file diff --git a/docs/content/docs/settings.md b/docs/content/docs/settings.md new file mode 100644 index 00000000..99ed1fab --- /dev/null +++ b/docs/content/docs/settings.md @@ -0,0 +1,8 @@ +--- +title: Settings +slug: settings +description: +date: '2021-08-30T16:13:00.546Z' +lastmod: '2021-08-30T16:13:01.763Z' +weight: 7 +--- \ No newline at end of file diff --git a/docs/lib/api.ts b/docs/lib/api.ts index 9f9ce183..2d657b5b 100644 --- a/docs/lib/api.ts +++ b/docs/lib/api.ts @@ -11,6 +11,7 @@ export function getPostSlugs(type: ContentType) { } export function getPostByFilename(type: ContentType, crntFile: string, fields: string[] = []) { + const realSlug = crntFile.replace(/\.md$/, ''); const fullPath = join(postsDirectory, type, `${realSlug}.md`) const fileContents = fs.readFileSync(fullPath, 'utf8') @@ -41,10 +42,12 @@ export function getPostByFilename(type: ContentType, crntFile: string, fields: s } export function getAllPosts(type: ContentType, fields: string[] = []) { - const fileNames = getPostSlugs(type) + const fileNames = getPostSlugs(type); + const posts = fileNames .map((fileName) => getPostByFilename(type, fileName, fields)) // sort posts by date in descending order - .sort((post1, post2) => ((post1 as any)?.date > (post2 as any)?.date ? -1 : 1)) + .sort((post1, post2) => ((post1 as any)?.date > (post2 as any)?.date ? -1 : 1)); + return posts } \ No newline at end of file diff --git a/docs/models/PageFrontMatter.ts b/docs/models/PageFrontMatter.ts new file mode 100644 index 00000000..0aa972b8 --- /dev/null +++ b/docs/models/PageFrontMatter.ts @@ -0,0 +1,9 @@ +export interface PageFrontMatter { + title: string; + slug: string; + description: string; + date: string; + lastmod: string; + content: string; + weight?: number; +} \ No newline at end of file diff --git a/docs/package-lock.json b/docs/package-lock.json index 794aa82a..3b474063 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -4508,6 +4508,49 @@ "unified": "^10.0.0" } }, + "remark-autolink-headings": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/remark-autolink-headings/-/remark-autolink-headings-7.0.1.tgz", + "integrity": "sha512-a1BIwoJ0cSnX+sPp5u3AFULBFWHGYBt57Fo4a+7IlGiJOQxs8b7uYAE5Iu26Ocl7Y5cvinZy3FaGVruLCKg6vA==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "extend": "^3.0.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "remark-heading-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/remark-heading-id/-/remark-heading-id-1.0.0.tgz", + "integrity": "sha512-86QaOiL+8jTV9P5Y0S25kSIcykCd/XmnqiFltWZRWKHmsVT4sevN7QJnkpUjkCJUpIeWte/LYH7pVlCTGz89fw==", + "requires": { + "unist-util-visit": "^1.4.0" + }, + "dependencies": { + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==" + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, "remark-html": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-14.0.0.tgz", diff --git a/docs/package.json b/docs/package.json index 9fdb4919..0d344d5e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,7 +20,9 @@ "react-dom": "17.0.2", "react-i18next": "^11.11.4", "react-markdown": "^7.0.1", - "rehype-raw": "^6.1.0" + "rehype-raw": "^6.1.0", + "remark-autolink-headings": "7.0.1", + "remark-heading-id": "1.0.0" }, "devDependencies": { "@types/react": "17.0.19", diff --git a/docs/pages/docs/[slug].tsx b/docs/pages/docs/[slug].tsx index 9b3d57d9..b7c8b230 100644 --- a/docs/pages/docs/[slug].tsx +++ b/docs/pages/docs/[slug].tsx @@ -1,86 +1,79 @@ import React from 'react'; -import ReactMarkdown from 'react-markdown'; import { getAllPosts, getPostByFilename } from '../../lib/api'; import { useRouter } from 'next/router'; -import rehypeRaw from 'rehype-raw'; import { Description, OtherMeta, Title } from '../../components/Meta'; import { Layout } from '../../components/Page/Layout'; +import { useTranslation } from 'react-i18next'; +import { Page } from '../../components/Docs/Page'; +import { Markdown } from '../../components/Docs/Markdown'; -export default function News({ page }: any) { +export default function Documentation({ page, pages }: any) { + const { t: strings } = useTranslation(); + const router = useRouter(); - const router = useRouter() if (!router.isFallback && !page?.slug) { return

Error

} return ( <> - - <Description value={page.description} /> - <OtherMeta image={page.image} type={`article`} /> + <Title value={strings(`documentation_title`)} /> + <Description value={`documentation_description`} /> + <OtherMeta image={`/assets/frontmatter-preview.png`} /> <Layout> - {/* eslint-disable react/no-children-prop */} - <ReactMarkdown - components={{ - a: ({node, ...props}) => { - const url = props?.href || ""; - const title = props?.children.length > 0 ? `${props?.children[0] as string}` : ""; - const elm = <a key={url as string} href={url as string} title={title}>{title}</a>; - return elm; - } - }} - rehypePlugins={[rehypeRaw]} - children={page.content} /> + <Page items={pages}> + <Markdown content={page?.content} /> + </Page> </Layout> </> ) } export async function getStaticProps({ params }: any) { - const blogItems = getAllPosts('docs', ['fileName', 'slug']); - const article: any = blogItems.find((b: any) => b.slug === params.slug); - - const blog: any = getPostByFilename('docs', article.fileName, [ + const pages = getAllPosts('docs', [ 'title', - 'date', - 'content', 'slug', - 'fileName', - 'category', 'description', - 'image' + 'date', + 'lastmod', + 'weight', + 'content', + 'fileName' + ]); + + const article: any = pages.find((b: any) => b.slug === params.slug); + + const doc: any = getPostByFilename('docs', article.fileName, [ + 'title', + 'slug', + 'description', + 'date', + 'lastmod', + 'weight', + 'content' ]) return { props: { page: { - ...blog - } + ...doc + }, + pages } } } export async function getStaticPaths() { - const blogItems = getAllPosts('docs', [ - 'title', - 'date', - 'slug', - 'fileName', - 'category', - 'description', - 'image' - ]) + const pages = getAllPosts('docs', ['slug', 'fileName']); return { - paths: blogItems.map((news: any) => { - return { - params: { - slug: news.slug, - fileName: news.fileName - } + paths: pages.map((page: any) => ({ + params: { + slug: page.slug, + fileName: page.fileName } - }), + })), fallback: false } } \ No newline at end of file diff --git a/docs/pages/docs/index.tsx b/docs/pages/docs/index.tsx index 4659c2cd..e0833ecd 100644 --- a/docs/pages/docs/index.tsx +++ b/docs/pages/docs/index.tsx @@ -2,14 +2,17 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; +import { Markdown } from '../../components/Docs/Markdown'; +import { Page } from '../../components/Docs/Page'; import { Description, OtherMeta, Title } from '../../components/Meta'; import { Layout } from '../../components/Page/Layout'; -import { Extension } from '../../constants/extension'; import { getAllPosts } from '../../lib/api'; -import { getPostByFilename } from '../../lib/api'; +import { PageFrontMatter } from '../../models/PageFrontMatter'; -export default function Home({ content }: any) { +export default function Home({ pages }: { pages: PageFrontMatter[] }) { const { t: strings } = useTranslation(); + + const welcome = pages?.find(p => p.slug === "index"); return ( <> @@ -18,32 +21,26 @@ export default function Home({ content }: any) { <OtherMeta image={`/assets/frontmatter-preview.png`} /> <Layout> - <div className="max-w-7xl mx-auto py-16 px-4 sm:px-6 lg:py-24 lg:px-8"> - <div className="text-6xl text-whisper-500 text-center"> - 🚧 Working on it 🚧 - </div> - - <div className="mt-8 text-lg"> - The new documentation format is currently being created, but you can currently find the Front Matter documenation on the <a className={`text-teal-500 hover:text-teal-700`} href={Extension.githubLink}>GitHub repository</a>. - </div> - </div> + <Page items={pages}> + <Markdown content={welcome?.content} /> + </Page> </Layout> </> ) } export const getStaticProps = async () => { - const blogItems = getAllPosts('docs', [ + const pages = getAllPosts('docs', [ 'title', - 'date', 'slug', - 'fileName', - 'category', 'description', - 'image' - ]) + 'date', + 'lastmod', + 'weight', + 'content' + ]); return { - props: { blogItems: blogItems.map(item => ({...item, type: 'blog'})) }, + props: { pages }, } } \ No newline at end of file diff --git a/docs/public/assets/register-folder.png b/docs/public/assets/register-folder.png new file mode 100644 index 00000000..6d5a73c4 Binary files /dev/null and b/docs/public/assets/register-folder.png differ diff --git a/docs/public/assets/welcome-light.png b/docs/public/assets/welcome-light.png new file mode 100644 index 00000000..8384ee13 Binary files /dev/null and b/docs/public/assets/welcome-light.png differ diff --git a/docs/public/assets/welcome-progress.png b/docs/public/assets/welcome-progress.png new file mode 100644 index 00000000..db21867d Binary files /dev/null and b/docs/public/assets/welcome-progress.png differ diff --git a/docs/styles/globals.css b/docs/styles/globals.css index fad82bdb..24cd742e 100644 --- a/docs/styles/globals.css +++ b/docs/styles/globals.css @@ -26,4 +26,44 @@ html, body {height: 100%;} a:hover { @apply text-teal-800; } +} + +.markdown { + @apply space-y-4; + + h1 { + @apply text-5xl tracking-tight font-extrabold sm:leading-none lg:text-5xl xl:text-6xl; + } + + h2 { + @apply tracking-tight font-extrabold sm:leading-none text-3xl xl:text-4xl; + } + + h3 { + @apply tracking-tight font-extrabold sm:leading-none text-xl xl:text-2xl; + } + + img { + @apply mx-auto my-8; + } + + p { + @apply my-4; + } + + ul { + @apply list-disc pl-4 my-4; + } + + a, a:visited { + @apply text-teal-500; + } + + a:hover { + @apply text-teal-800; + } + + blockquote { + @apply py-2 px-4 my-4 border-l-4 border-teal-500; + } } \ No newline at end of file