aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-10-28 02:44:33 -0700
committerPinapelz <yukais@pinapelz.com>2025-10-28 02:44:44 -0700
commit5fea6ece5c4c90753e19c27150522678b1872a12 (patch)
tree51f08a6e294e5a60fa0e6af38c71a0e5981853a0
parent29a0ab1f2b04a9b9a45282b6c9026136b1ed1f6d (diff)
pre-parse headers and add external links metadata tag for sidemenu
-rw-r--r--include/markdown_translator.hpp18
-rw-r--r--src/markdown_translator.cpp71
-rw-r--r--styles/carbon.css24
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 */
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage