diff --git a/docusaurus.config.js b/docusaurus.config.js
index fbed23c6c..522306cc0 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -79,6 +79,36 @@ module.exports = {
logo: "https://keploy.io/docs/img/favicon.png",
}),
},
+ {
+ tagName: "script",
+ attributes: {
+ type: "application/ld+json",
+ },
+ innerHTML: JSON.stringify({
+ "@context": "https://schema.org/",
+ "@type": "Organization",
+ name: "Keploy",
+ url: "https://keploy.io/",
+ logo: "https://keploy.io/docs/img/favicon.png",
+ }),
+ },
+ {
+ tagName: "script",
+ attributes: {
+ type: "application/ld+json",
+ },
+ innerHTML: JSON.stringify({
+ "@context": "https://schema.org/",
+ "@type": "WebSite",
+ name: "Keploy Documentation",
+ url: "https://keploy.io/docs/",
+ potentialAction: {
+ "@type": "SearchAction",
+ target: "https://keploy.io/docs/search?q={search_term_string}",
+ "query-input": "required name=search_term_string",
+ },
+ }),
+ },
],
colorMode: {
defaultMode: "light",
diff --git a/src/theme/DocBreadcrumbs/index.js b/src/theme/DocBreadcrumbs/index.js
new file mode 100644
index 000000000..a4c375751
--- /dev/null
+++ b/src/theme/DocBreadcrumbs/index.js
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from "react";
+import clsx from "clsx";
+import {ThemeClassNames} from "@docusaurus/theme-common";
+import {useSidebarBreadcrumbs} from "@docusaurus/plugin-content-docs/client";
+import {useHomePageRoute} from "@docusaurus/theme-common/internal";
+import {useLocation} from "@docusaurus/router";
+import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
+import Link from "@docusaurus/Link";
+import {translate} from "@docusaurus/Translate";
+import Head from "@docusaurus/Head";
+import HomeBreadcrumbItem from "@theme/DocBreadcrumbs/Items/Home";
+
+import styles from "./styles.module.css";
+
+function BreadcrumbsItemLink({children, href, isLast}) {
+ const className = "breadcrumbs__link";
+ if (isLast) {
+ return (
+
+ {children}
+
+ );
+ }
+ return href ? (
+
+ {children}
+
+ ) : (
+ {children}
+ );
+}
+
+function BreadcrumbsItem({children, active, index, addMicrodata}) {
+ return (
+
+ {children}
+
+
+ );
+}
+
+export default function DocBreadcrumbs() {
+ const breadcrumbs = useSidebarBreadcrumbs();
+ const homePageRoute = useHomePageRoute();
+ const {siteConfig} = useDocusaurusContext();
+ const {pathname} = useLocation();
+
+ if (!breadcrumbs) {
+ return null;
+ }
+
+ const toAbsoluteUrl = (baseUrl, url) => {
+ if (!url) {
+ return null;
+ }
+ if (url.startsWith("http://") || url.startsWith("https://")) {
+ return url;
+ }
+ const trimmedBase = baseUrl?.replace(/\/$/, "") ?? "";
+ const normalizedPath = url.startsWith("/") ? url : `/${url}`;
+ return `${trimmedBase}${normalizedPath}`;
+ };
+
+ const breadcrumbItems = [];
+ const pushBreadcrumbItem = (item) => {
+ if (!item?.item || !item?.name) {
+ return;
+ }
+ if (breadcrumbItems.some((existing) => existing.item === item.item)) {
+ return;
+ }
+ breadcrumbItems.push(item);
+ };
+
+ if (siteConfig?.url) {
+ pushBreadcrumbItem({name: "Home", item: siteConfig.url});
+ }
+
+ if (siteConfig?.url && siteConfig?.baseUrl) {
+ const docsUrl = toAbsoluteUrl(siteConfig.url, siteConfig.baseUrl);
+ if (docsUrl && docsUrl !== siteConfig.url) {
+ pushBreadcrumbItem({name: "Docs", item: docsUrl});
+ }
+ }
+
+ if (breadcrumbs.length > 0) {
+ breadcrumbs.forEach((crumb, index) => {
+ const isLast = index === breadcrumbs.length - 1;
+ const href = crumb.href || (isLast ? pathname : null);
+ const absoluteUrl = toAbsoluteUrl(siteConfig?.url, href);
+ if (!absoluteUrl) {
+ return;
+ }
+ pushBreadcrumbItem({
+ name: crumb.label,
+ item: absoluteUrl,
+ });
+ });
+ }
+
+ const breadcrumbSchema =
+ breadcrumbItems.length > 0
+ ? {
+ "@context": "https://schema.org",
+ "@type": "BreadcrumbList",
+ itemListElement: breadcrumbItems.map((item, index) => ({
+ "@type": "ListItem",
+ position: index + 1,
+ name: item.name,
+ item: item.item,
+ })),
+ }
+ : null;
+
+ return (
+ <>
+ {breadcrumbSchema && (
+
+
+
+ )}
+
+ >
+ );
+}
diff --git a/src/theme/DocBreadcrumbs/styles.module.css b/src/theme/DocBreadcrumbs/styles.module.css
new file mode 100644
index 000000000..a400c5d9e
--- /dev/null
+++ b/src/theme/DocBreadcrumbs/styles.module.css
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+.breadcrumbsContainer {
+ --ifm-breadcrumb-size-multiplier: 0.8;
+ margin-bottom: 0.8rem;
+}
diff --git a/src/theme/DocItem/index.js b/src/theme/DocItem/index.js
index 34a12f682..89547be39 100644
--- a/src/theme/DocItem/index.js
+++ b/src/theme/DocItem/index.js
@@ -19,13 +19,14 @@ import DocBreadcrumbs from "@theme/DocBreadcrumbs";
import Layout from "@docusaurus/core/lib/client/theme-fallback/Layout";
import Head from "@docusaurus/Head";
import MDXContent from "@theme/MDXContent";
+import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import {KeployCloud} from "@site/src/components/KeployCloud";
export default function DocItem(props) {
const {content: DocContent} = props;
const {metadata, frontMatter, assets} = DocContent;
const {
- keywords,
+ keywords: frontMatterKeywords,
hide_title: hideTitle,
hide_table_of_contents: hideTableOfContents,
toc_min_heading_level: tocMinHeadingLevel,
@@ -43,19 +44,138 @@ export default function DocItem(props) {
!hideTableOfContents && DocContent.toc && DocContent.toc.length > 0;
const renderTocDesktop =
canRenderTOC && (windowSize === "desktop" || windowSize === "ssr");
-
+ const {siteConfig} = useDocusaurusContext();
+ const toIsoDate = (value) => {
+ if (!value) {
+ return null;
+ }
+ const isEpochSeconds = Number.isFinite(value) && value < 1e12;
+ const date = new Date(isEpochSeconds ? value * 1000 : value);
+ if (Number.isNaN(date.getTime())) {
+ return null;
+ }
+ return date.toISOString();
+ };
+ const toAbsoluteUrl = (baseUrl, url) => {
+ if (!url) {
+ return null;
+ }
+ if (url.startsWith("http://") || url.startsWith("https://")) {
+ return url;
+ }
+ const trimmedBase = baseUrl?.replace(/\/$/, "") ?? "";
+ const normalizedPath = url.startsWith("/") ? url : `/${url}`;
+ return `${trimmedBase}${normalizedPath}`;
+ };
+ const toArray = (value) => {
+ if (!value) {
+ return [];
+ }
+ return Array.isArray(value) ? value : [value];
+ };
+ const toPersonList = (value) => {
+ return toArray(value)
+ .map((item) => {
+ if (!item) {
+ return null;
+ }
+ if (typeof item === "string") {
+ return {["@type"]: "Person", name: item};
+ }
+ if (item.name) {
+ return {
+ "@type": "Person",
+ name: item.name,
+ ...(item.url ? {url: item.url} : {}),
+ };
+ }
+ return null;
+ })
+ .filter(Boolean);
+ };
+ const pageUrl = toAbsoluteUrl(siteConfig?.url, metadata?.permalink);
+ const modifiedTime = toIsoDate(
+ metadata?.lastUpdatedAt || frontMatter?.lastUpdatedAt
+ );
+ const publishedTime = toIsoDate(frontMatter?.date || frontMatter?.publishedAt);
+ const schemaTypeFromFrontMatter =
+ frontMatter?.schemaType || frontMatter?.schema_type;
+ const isApi =
+ frontMatter?.apiReference === true ||
+ frontMatter?.type === "api" ||
+ (frontMatter?.tags || []).includes?.("api");
+ const isBlog =
+ frontMatter?.type === "blog" ||
+ frontMatter?.blog === true ||
+ (frontMatter?.tags || []).includes?.("blog");
+ const schemaType = schemaTypeFromFrontMatter
+ ? schemaTypeFromFrontMatter
+ : isApi
+ ? "APIReference"
+ : isBlog
+ ? "BlogPosting"
+ : "Article";
+ const authorList = toPersonList(frontMatter?.author || frontMatter?.authors);
+ const maintainerList = toPersonList(frontMatter?.maintainer);
+ const contributorList = toPersonList(frontMatter?.contributor);
+ const combinedContributors = [...maintainerList, ...contributorList];
+ const keywords = frontMatter?.keywords || metadata?.keywords;
+ const metaKeywords = frontMatterKeywords ?? metadata?.keywords;
+ const programmingLanguage =
+ frontMatter?.programmingLanguage || frontMatter?.programmingLanguages;
+ const targetPlatform = frontMatter?.targetPlatform;
+ const proficiencyLevel = frontMatter?.proficiencyLevel;
+ const articleSchema =
+ pageUrl && title
+ ? {
+ "@context": "https://schema.org",
+ "@type": schemaType,
+ headline: title,
+ description,
+ ...(modifiedTime ? {dateModified: modifiedTime} : {}),
+ ...(publishedTime ? {datePublished: publishedTime} : {}),
+ ...(keywords ? {keywords} : {}),
+ ...(authorList.length ? {author: authorList} : {}),
+ ...(combinedContributors.length
+ ? {contributor: combinedContributors}
+ : {}),
+ ...(proficiencyLevel ? {proficiencyLevel} : {}),
+ ...(programmingLanguage ? {programmingLanguage} : {}),
+ ...(targetPlatform ? {targetPlatform} : {}),
+ mainEntityOfPage: {
+ "@type": "WebPage",
+ "@id": pageUrl,
+ },
+ publisher: {
+ "@type": "Organization",
+ name: "Keploy",
+ logo: {
+ "@type": "ImageObject",
+ url: "https://keploy.io/docs/img/favicon.png",
+ },
+ },
+ }
+ : null;
const MDXComponent = props.content;
return (
<>
{title}
{description && }
+ {modifiedTime && (
+
+ )}
+ {articleSchema && (
+
+ )}