diff options
| author | Pinapelz <yukais@pinapelz.com> | 2025-10-28 02:44:33 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2025-10-28 02:44:44 -0700 |
| commit | 5fea6ece5c4c90753e19c27150522678b1872a12 (patch) | |
| tree | 51f08a6e294e5a60fa0e6af38c71a0e5981853a0 | |
| parent | 29a0ab1f2b04a9b9a45282b6c9026136b1ed1f6d (diff) | |
pre-parse headers and add external links metadata tag for sidemenu
| -rw-r--r-- | include/markdown_translator.hpp | 18 | ||||
| -rw-r--r-- | src/markdown_translator.cpp | 71 | ||||
| -rw-r--r-- | styles/carbon.css | 24 |
3 files changed, 100 insertions, 13 deletions
diff --git a/include/markdown_translator.hpp b/include/markdown_translator.hpp index 8bf6c84..65eb545 100644 --- a/include/markdown_translator.hpp +++ b/include/markdown_translator.hpp @@ -16,10 +16,22 @@ public: std::string translate(const std::string& markdownContent); std::string processLine(const std::string& line); + void addExternalMenuItem(const std::string& name, const std::string& link){ + ExternalMenuItem menuItem{name, link}; + externalMenuLinks.push_back(menuItem); + } enum Theme { carbon }; + struct ExternalMenuItem{ + std::string name; + std::string link; + }; + + std::vector<ExternalMenuItem> externalMenuLinks; + + private: // Regex for various tags const std::string headerRegexStr{"^(#{1,6})\\s+(.*)$"}; @@ -37,7 +49,8 @@ private: std::string processSingleFigure(const std::string& text); std::string processFigureBlock(const std::vector<std::string>& lines); // Navigation and table of contents - void generateSideBar(std::stringstream& output, const std::vector<std::string>& headers); + void prescanHeaders(std::stringstream& markdownStream); + void generateSideBar(std::stringstream& output); std::string createAnchorId(const std::string& text); // Utility functions @@ -80,6 +93,7 @@ private: switch(theme){ case Theme::carbon: cssPath = "styles/carbon.css"; + break; default: cssPath = "styles/carbon.css"; } @@ -88,6 +102,8 @@ private: // Member variables std::string title; std::string cssPath{"styles/carbon.css"}; + std::vector<std::string> headers; // h1-6 + }; #endif // MARKDOWN_TRANSLATOR_H diff --git a/src/markdown_translator.cpp b/src/markdown_translator.cpp index 5bd1cc4..e9ddcae 100644 --- a/src/markdown_translator.cpp +++ b/src/markdown_translator.cpp @@ -3,15 +3,36 @@ #include <iostream> #include <algorithm> -MarkdownTranslator::MarkdownTranslator() : title("WikiMD2HTML") { +MarkdownTranslator::MarkdownTranslator() : title("WikiMD2HTML"), cssPath("styles/carbon.css") { } MarkdownTranslator::~MarkdownTranslator() { } +void MarkdownTranslator::prescanHeaders(std::stringstream& markdownStream) { + auto savedPos = markdownStream.tellg(); + markdownStream.seekg(0); + std::regex headerRegex(headerRegexStr); + std::smatch matches; + std::string line; + while (std::getline(markdownStream, line)) { + if (std::regex_match(line, matches, headerRegex)) { + int level = matches[1].length(); + std::string content = matches[2]; + headers.push_back(std::to_string(level) + ":" + content); + } + } + markdownStream.clear(); + markdownStream.seekg(savedPos); +} + + void MarkdownTranslator::processMetadata(const std::vector<std::string>& lines){ // format of keys -> key: value + int externalLinkBuilderState{0}; + std::string currExternalLinkName; + std::string currExternalLinkUrl; for(std::string currentLine : lines){ size_t colonPos = currentLine.find(':'); if (colonPos != std::string::npos) { @@ -21,6 +42,30 @@ void MarkdownTranslator::processMetadata(const std::vector<std::string>& lines){ if (key == "title") { title = value; } + else if (key == "external-name") { + currExternalLinkName = value; + externalLinkBuilderState = 1; + } + else if (key == "external-url") { + if (externalLinkBuilderState != 0) { + currExternalLinkUrl = value; + if (!currExternalLinkName.empty() && !currExternalLinkUrl.empty()) { + addExternalMenuItem(currExternalLinkName, currExternalLinkUrl); + currExternalLinkName = ""; + currExternalLinkUrl = ""; + externalLinkBuilderState = 0; + } + } + else{ + std::cout << "[WARNING] Unexpected external-url \"" << value << "\" found with no matching external-name. Ignoring..."; + } + } + else { + std::cerr << "ERROR OCCURED IN METADATA. UNKNOWN KEY" << std::endl; + } + if (key == "title") { + title = value; + } } } } @@ -29,7 +74,6 @@ std::string MarkdownTranslator::translate(const std::string& markdownContent) { std::stringstream htmlOutput; std::stringstream markdownStream(markdownContent); std::string line; - std::vector<std::string> headers; std::string currentLine; // Parse metadata section if it exists @@ -48,8 +92,12 @@ std::string MarkdownTranslator::translate(const std::string& markdownContent) { // Process and apply metadata to curr object if(metadataExists) processMetadata(metadataLines); + + // Pre-scan for headers before generating sidebar + prescanHeaders(markdownStream); + htmlOutput << buildHTMLHeader(title); - generateSideBar(htmlOutput, headers); + generateSideBar(htmlOutput); // Main content container htmlOutput << " <div class=\"main-content\">\n"; htmlOutput << " <div class=\"container\">\n"; @@ -81,23 +129,24 @@ std::string MarkdownTranslator::translate(const std::string& markdownContent) { } + return htmlOutput.str(); } -void MarkdownTranslator::generateSideBar(std::stringstream& output, const std::vector<std::string>& headers) { +void MarkdownTranslator::generateSideBar(std::stringstream& output) { output << " <div class=\"nav-sidebar\">\n"; output << " <div class=\"nav-logo\">\n"; output << " <h3>" + title + "</h3>\n"; output << " </div>\n"; output << " <ul class=\"nav-menu\">\n"; - + if(externalMenuLinks.size() > 0){ + for(const ExternalMenuItem& menuItem : externalMenuLinks){ + output << " <li><a href=\""+menuItem.link+"\">"+menuItem.name+"</a></li>\n"; + } + } output << " <h4>Table of Contents</h4>\n"; - output << " <li><a href=\"index.html\">Home</a></li>\n"; - output << " <li><a href=\"#\">Recent Changes</a></li>\n"; - output << " <li><a href=\"#\">Random Page</a></li>\n"; output << " <ul class=\"nav-submenu\">\n"; - // Generate navigation from headers for (const auto& header : headers) { size_t separatorPos = header.find(':'); if (separatorPos != std::string::npos) { @@ -108,12 +157,12 @@ void MarkdownTranslator::generateSideBar(std::stringstream& output, const std::v std::string anchorId = createAnchorId(content); // Add indentation based on header level - std::string indentation = ""; + std::string indentation = " "; for (int i = 1; i < level; ++i) { indentation += " "; } - output << " " << indentation << "<li><a href=\"#" << anchorId << "\">" << content << "</a></li>\n"; + output << indentation << "<li class=\"level-" << level << "\"><a href=\"#" << anchorId << "\">" << content << "</a></li>\n"; } } diff --git a/styles/carbon.css b/styles/carbon.css index a6b6228..e857486 100644 --- a/styles/carbon.css +++ b/styles/carbon.css @@ -106,7 +106,29 @@ body { .nav-submenu { list-style-type: none; - padding-left: 15px; + padding: 0; + margin: 0; +} + +/* Indentation for nested submenu items */ +.nav-submenu li.level-2 a { + padding-left: 30px; +} + +.nav-submenu li.level-3 a { + padding-left: 40px; +} + +.nav-submenu li.level-4 a { + padding-left: 50px; +} + +.nav-submenu li.level-5 a { + padding-left: 60px; +} + +.nav-submenu li.level-6 a { + padding-left: 70px; } /* Main content */ |
