From 39010822204f6c5ec1fd75b5ce91fc0390372258 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 Sep 2025 05:58:33 +0530 Subject: [PATCH] added new fatures --- docs/index.mdx | 3 +- docusaurus.config.js | 31 +++++- package-lock.json | 6 ++ package.json | 1 + src/theme/DocItem/DocContent.js | 113 +++++++++++++++++++ src/theme/DocItem/DocsInfo.js | 89 +++++++++++++++ src/theme/DocItem/DocsRating.js | 82 ++++++++++++++ src/theme/DocItem/Layout.js | 61 +++++++++++ src/theme/DocItem/ShareButton.js | 75 +++++++++++++ src/theme/DocItem/index.js | 25 +++++ src/theme/DocItem/shareButton.module.css | 67 ++++++++++++ src/theme/DocItem/styles.module.css | 131 +++++++++++++++++++++++ 12 files changed, 677 insertions(+), 7 deletions(-) create mode 100644 src/theme/DocItem/DocContent.js create mode 100644 src/theme/DocItem/DocsInfo.js create mode 100644 src/theme/DocItem/DocsRating.js create mode 100644 src/theme/DocItem/Layout.js create mode 100644 src/theme/DocItem/ShareButton.js create mode 100644 src/theme/DocItem/index.js create mode 100644 src/theme/DocItem/shareButton.module.css create mode 100644 src/theme/DocItem/styles.module.css diff --git a/docs/index.mdx b/docs/index.mdx index 890e4e6..ca57e99 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -1,11 +1,10 @@ --- +title: Welcome to CodeHarborHub Tutorials sidebar_label: Welcome to CodeHarborHub sidebar_position: 1 slug: / --- -# Welcome to CodeHarborHub Tutorials - Hello, and welcome to CodeHarborHub! Our mission is to provide accessible and comprehensive educational resources to learners of all levels, from beginners to advanced professionals. Whether you're looking to kickstart your career in web development, master a new programming language, or stay updated on the latest tech trends, we've got you covered. diff --git a/docusaurus.config.js b/docusaurus.config.js index 436b637..0eb1760 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -41,6 +41,9 @@ const config = { editUrl: "https://github.com/codeharborhub/tutorial/edit/main/", remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], + numberPrefixParser: false, + showLastUpdateAuthor: true, + showLastUpdateTime: true, }, theme: { customCss: "./src/css/custom.css", @@ -124,11 +127,29 @@ const config = { target: "_self", }, items: [ - // { - // to: "/", - // position: "right", - // label: "Tutorial", - // }, + { + type: "dropdown", + html: '📚 Tutorials', + position: "left", + items: [ + { + type: "html", + value: ``, + }, + ], + }, { href: "https://github.com/codeharborhub", position: "right", diff --git a/package-lock.json b/package-lock.json index 2613bde..87e1867 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "react-redux": "^9.1.0", "react-simple-code-editor": "^0.14.0", "react-window": "^1.8.10", + "reading-time": "^1.5.0", "recharts": "^2.12.7", "redux": "^5.0.1", "rehype-katex": "^7.0.0", @@ -22761,6 +22762,11 @@ "node": ">=8.10.0" } }, + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, "node_modules/recharts": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", diff --git a/package.json b/package.json index efc475a..217014b 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react-redux": "^9.1.0", "react-simple-code-editor": "^0.14.0", "react-window": "^1.8.10", + "reading-time": "^1.5.0", "recharts": "^2.12.7", "redux": "^5.0.1", "rehype-katex": "^7.0.0", diff --git a/src/theme/DocItem/DocContent.js b/src/theme/DocItem/DocContent.js new file mode 100644 index 0000000..84568b6 --- /dev/null +++ b/src/theme/DocItem/DocContent.js @@ -0,0 +1,113 @@ +import React from "react"; +import Head from "@docusaurus/Head"; +import MDXComponents from "../MDXComponents"; +import { MDXProvider } from "@mdx-js/react"; +import { + useDoc, + useDocsVersion, +} from "@docusaurus/plugin-content-docs/client"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import useBaseUrl from "@docusaurus/useBaseUrl"; +import DocPaginator from "@theme/DocPaginator"; +// import DocVersionBanner from "@theme/DocVersionBanner"; +import TOC from "@theme/TOC"; +import clsx from "clsx"; +import styles from "./styles.module.css"; +import DocsInfo from "./DocsInfo"; +import DocsRating from "./DocsRating"; + +export const DocContent = ({ Content, contentRef, readingTimeInWords }) => { + const { siteConfig } = useDocusaurusContext(); + const { + metadata, + frontMatter: { + image: metaImage, + keywords, + hide_title: hideTitle, + hide_table_of_contents: hideTableOfContents, + }, + toc, + } = useDoc(); + + const { url: siteUrl } = siteConfig; + // const versionMetadata = useDocsVersion(); + const { + description, + title, + permalink, + editUrl, + lastUpdatedAt, + lastUpdatedBy, + unversionedId, + } = metadata; + + const metaImageUrl = useBaseUrl(metaImage, { + absolute: true, + }); + + return ( + <> + + {description && } + {description && ( + + )} + {keywords && keywords.length && ( + + )} + {metaImage && } + {metaImage && } + {metaImage && ( + + )} + {permalink && } + {permalink && } + + +
+
+ {/* */} +
+
+ {!hideTitle && ( +
+

{title}

+
+ )} + {(editUrl || lastUpdatedAt || lastUpdatedBy) && ( + + )} + +
+ +
+
+
+ +
+ +
+
+ +
+
+
+ {!hideTableOfContents && toc && ( +
+ +
+ )} +
+ + ); +}; \ No newline at end of file diff --git a/src/theme/DocItem/DocsInfo.js b/src/theme/DocItem/DocsInfo.js new file mode 100644 index 0000000..028aa56 --- /dev/null +++ b/src/theme/DocItem/DocsInfo.js @@ -0,0 +1,89 @@ +import React from "react"; +import { useLocation } from "react-router-dom"; +import { + FiEdit3, + FiPrinter, + FiAlertCircle, + FiClock, + FiUser, +} from "react-icons/fi"; +import styles from "./styles.module.css"; +import ShareButton from "./ShareButton"; + +function DocsInfo({ docsPluginId, ...props }) { + const location = useLocation(); + const openDocIssueURL = + "https://github.com/codeharborhub/codeharborhub.github.io/issues/new?assignees=&labels=&template=---doc-error-report.md&title=Issue with codeharborhub.github.io" + + `${location.pathname}`; + + return ( +
+
+ {/* Left Section – Meta Info */} + {(props.lastUpdatedAt || props.lastUpdatedBy) && ( +
+ {props.lastUpdatedAt && ( + + + + + )} + {props.readingTimeInWords && ( + + ⏱ {props.readingTimeInWords} + + )} + {props.lastUpdatedBy && ( + + + {props.lastUpdatedBy} + + )} +
+ )} + + {/* Right Section – Actions */} +
+ {props.editUrl && ( + + + Edit + + )} + + + + {openDocIssueURL && ( + + + Report + + )} + + +
+
+
+ ); +} + +export default DocsInfo; \ No newline at end of file diff --git a/src/theme/DocItem/DocsRating.js b/src/theme/DocItem/DocsRating.js new file mode 100644 index 0000000..9925068 --- /dev/null +++ b/src/theme/DocItem/DocsRating.js @@ -0,0 +1,82 @@ +import React, { useState } from "react"; +import { useLocation } from "react-router-dom"; +import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment"; +// import { useColorMode } from "@docusaurus/theme-common"; +import { FiThumbsUp, FiThumbsDown } from "react-icons/fi"; +import styles from "./styles.module.css"; + +const DocsRating = ({ label }) => { + if (!ExecutionEnvironment.canUseDOM) return null; + + const location = useLocation(); + // const { colorMode } = useColorMode(); + const DiscordInviteURL = "https://discord.gg/8p9Z6jkVru"; + const openDocIssueURL = + `https://github.com/codeharborhub/codeharborhub.github.io/issues/new?assignees=&labels=&template=---doc-error-report.md&title=Issue with codeharborhub.github.io${location.pathname}`; + const docEnhancementURL = + `https://github.com/codeharborhub/codeharborhub.github.io/issues/new?assignees=&labels=&template=---doc-site-enhancement-request.md&title=Doc enhancement request for codeharborhub.github.io${location.pathname}`; + + const [haveVoted, setHaveVoted] = useState(false); + const [liked, setLiked] = useState(false); + + const giveFeedback = (value) => { + if (window.ga) { + window.ga("send", { + hitType: "event", + eventCategory: "button", + eventAction: "feedback", + eventLabel: label, + eventValue: value, + }); + } + setLiked(value === 1); + setHaveVoted(true); + }; + + return ( +
+ {haveVoted ? ( + liked ? ( +
🎉 Thanks for letting us know!
+ ) : ( +
+

Thanks for your feedback! Need help or have suggestions?

+

+ • Ask a question on our{" "} + + Discord Channel + +
+ • Report a problem
+ • Suggest an improvement +

+
+ ) + ) : ( +
+

Was this topic helpful?

+
+ + +
+
+ )} +
+ ); +}; + +export default DocsRating; diff --git a/src/theme/DocItem/Layout.js b/src/theme/DocItem/Layout.js new file mode 100644 index 0000000..d20a731 --- /dev/null +++ b/src/theme/DocItem/Layout.js @@ -0,0 +1,61 @@ +//Swizzled this component to fix TOC sidebar not showing on small screens + +import React from 'react'; +import clsx from 'clsx'; +import {useWindowSize} from '@docusaurus/theme-common'; +import {useDoc} from '@docusaurus/plugin-content-docs/client'; +import DocItemPaginator from '@theme/DocItem/Paginator'; +import DocVersionBanner from '@theme/DocVersionBanner'; +import DocVersionBadge from '@theme/DocVersionBadge'; +import DocItemFooter from '@theme/DocItem/Footer'; +import DocItemTOCMobile from '@theme/DocItem/TOC/Mobile'; +import DocItemTOCDesktop from '@theme/DocItem/TOC/Desktop'; +import DocItemContent from '@theme/DocItem/Content'; +import DocBreadcrumbs from '@theme/DocBreadcrumbs'; +import Unlisted from '@theme/Unlisted'; +import styles from './styles.module.css'; + +/** + * Decide if the toc should be rendered, on mobile or desktop viewports + */ +function useDocTOC() { + const {frontMatter, toc} = useDoc(); + const windowSize = useWindowSize({ desktopBreakpoint: 1150 }); + const hidden = frontMatter.hide_table_of_contents; + const canRender = !hidden && toc.length > 0; + const mobile = canRender ? : undefined; + const desktop = + canRender && (windowSize === 'desktop' || windowSize === 'ssr') ? ( + + ) : undefined; + return { + hidden, + mobile, + desktop, + }; +} +export default function DocItemLayout({children}) { + const docTOC = useDocTOC(); + const { + metadata: {unlisted}, + } = useDoc(); + return ( +
+
+ {unlisted && } + +
+
+ + + {docTOC.mobile} + {children} + +
+ +
+
+ {docTOC.desktop &&
{docTOC.desktop}
} +
+ ); +} \ No newline at end of file diff --git a/src/theme/DocItem/ShareButton.js b/src/theme/DocItem/ShareButton.js new file mode 100644 index 0000000..d5882a1 --- /dev/null +++ b/src/theme/DocItem/ShareButton.js @@ -0,0 +1,75 @@ +import React from "react"; +import { useLocation } from "react-router-dom"; +import { useColorMode } from "@docusaurus/theme-common"; +import { + FiShare2, + FiTwitter, + FiLinkedin, + FiFacebook, + FiMail, +} from "react-icons/fi"; +import styles from "./shareButton.module.css"; + +function ShareButton({ title }) { + const location = useLocation(); + const { colorMode } = useColorMode(); + const baseUrl = "https://codeharborhub.github.io"; + const fullUrl = baseUrl + location.pathname; + + const shareLinks = [ + { + name: "Twitter", + url: `https://twitter.com/share?url=${fullUrl}&text=Check out this article on ${title}&hashtags=codeharborhub,opensource`, + icon: , + color: "#1DA1F2", + }, + { + name: "LinkedIn", + url: `https://www.linkedin.com/shareArticle?mini=true&url=${fullUrl}&source=CodeHarborHub`, + icon: , + color: "#0A66C2", + }, + { + name: "Facebook", + url: `https://www.facebook.com/sharer/sharer.php?u=${fullUrl}`, + icon: , + color: "#1877F2", + }, + { + name: "Email", + url: `mailto:?subject=Shared Article | ${title} | CodeHarborHub Docs&body=Check out this article on ${title}: ${fullUrl}`, + icon: , + color: "#D44638", + }, + ]; + + return ( +
+ + + +
+ ); +} + +export default ShareButton; \ No newline at end of file diff --git a/src/theme/DocItem/index.js b/src/theme/DocItem/index.js new file mode 100644 index 0000000..7cfe71d --- /dev/null +++ b/src/theme/DocItem/index.js @@ -0,0 +1,25 @@ +import React, { useEffect, useRef, useState } from "react"; +import readingTime from "reading-time/lib/reading-time"; +import { DocProvider } from "@docusaurus/plugin-content-docs/client"; + +//Components +import { DocContent } from "./DocContent"; + +function DocItem(props) { + const contentRef = useRef(); + const [readingTimeInWords, setReadingTimeInWords] = useState(""); + + useEffect(() => { + if (contentRef.current) { + const readTime = readingTime(contentRef.current.innerText); + setReadingTimeInWords(readTime.text); + } + }, [contentRef]); + return ( + + + + ); +} + +export default DocItem; \ No newline at end of file diff --git a/src/theme/DocItem/shareButton.module.css b/src/theme/DocItem/shareButton.module.css new file mode 100644 index 0000000..013b5cf --- /dev/null +++ b/src/theme/DocItem/shareButton.module.css @@ -0,0 +1,67 @@ +.dropdown { + position: relative; + display: inline-block; +} + +.trigger { + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.4rem 0.7rem; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + background: var(--ifm-background-surface-color); + cursor: pointer; + color: var(--ifm-font-color-base); + transition: background 0.2s; + font-size: 0.9rem; +} + +.trigger:hover { + background: var(--ifm-color-emphasis-100); +} + +.shareIcon { + font-size: 1rem; +} + +.menu { + display: none; + position: absolute; + top: 100%; + right: 0; + margin-top: 0.4rem; + padding: 0.4rem 0; + background: var(--ifm-background-surface-color); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + min-width: 160px; + z-index: 20; + list-style: none; +} + +.dropdown:hover .menu { + display: block; +} + +.menuItem { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.6rem 0.8rem; + color: var(--ifm-font-color-base); + text-decoration: none; + transition: background 0.2s, color 0.2s; + font-size: 0.9rem; +} + +.menuItem:hover { + background: var(--ifm-color-emphasis-100); + color: var(--hover-color); + text-decoration: none; +} + +.icon { + font-size: 1rem; +} diff --git a/src/theme/DocItem/styles.module.css b/src/theme/DocItem/styles.module.css new file mode 100644 index 0000000..d529880 --- /dev/null +++ b/src/theme/DocItem/styles.module.css @@ -0,0 +1,131 @@ +.docTitle { + font-size: 3rem; + margin-bottom: -calc(var(--ifm-leading-desktop) * var(--ifm-leading)); + /* text-align: justify; */ +} + +.docsInfoWrapper { + width: 100%; + display: flex; + justify-content: center; +} + +.docsInfoContainer { + width: 100%; + padding: 1rem 0.2rem; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + border-radius: 8px; +} + +.metaInfo { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + color: var(--ifm-color-content-secondary); + font-size: 0.9rem; +} + +.metaItem { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.actions { + display: flex; + align-items: center; + gap: 0.8rem; + margin-top: 0.6rem; +} + +.actionBtn { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.4rem 0.75rem; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + background: var(--ifm-background-surface-color); + font-size: 0.85rem; + color: var(--ifm-font-color-base); + text-decoration: none; + cursor: pointer; + transition: all 0.2s ease; +} + +.actionBtn:hover { + background: var(--ifm-color-primary); + color: #fff; + border-color: var(--ifm-color-primary); +} + +.icon { + font-size: 1rem; + vertical-align: middle; +} + +.docsRating { + max-width: 600px; + text-align: center; + font-family: var(--ifm-font-family-base); +} + +.heading { + font-size: 1.2rem; + margin-bottom: 1rem; +} + +.buttonGroup { + display: flex; + justify-content: center; + gap: 1rem; + flex-wrap: wrap; +} + +.voteBtn { + display: flex; + align-items: center; + gap: 0.5rem; + background: var(--ifm-background-surface-color); + border: 1px solid var(--ifm-color-emphasis-200); + color: var(--ifm-font-color-base); + padding: 0.6rem 1.2rem; + border-radius: 6px; + cursor: pointer; + transition: background 0.2s, color 0.2s, border 0.2s; + font-size: 0.95rem; +} + +.voteBtn:hover { + background: var(--ifm-color-emphasis-100); + border-color: var(--ifm-color-primary); + color: var(--ifm-color-primary); +} + +.icon { + font-size: 1.1rem; +} + +.thankYou { + font-size: 1.1rem; + color: var(--ifm-color-success); + font-weight: 500; +} + +.feedbackLinks { + text-align: left; + margin-top: 0.5rem; + line-height: 1.6; +} + +.feedbackLinks a { + color: var(--ifm-color-primary); + text-decoration: none; +} + +.feedbackLinks a:hover { + text-decoration: underline; +}