Enhance documentation viewer with code highlighting and update metadata handling

This commit is contained in:
2025-02-04 14:50:29 +11:00
parent 36333e8821
commit 2d640e5248
7 changed files with 150 additions and 70 deletions

View File

@@ -26,5 +26,6 @@
"workspaces": false, "workspaces": false,
"file-recovery": true, "file-recovery": true,
"publish": false, "publish": false,
"sync": false "sync": false,
"webviewer": false
} }

View File

@@ -20,8 +20,21 @@
"icon": "lucide-file", "icon": "lucide-file",
"title": "DeepImpact" "title": "DeepImpact"
} }
},
{
"id": "7899dc85e8880db2",
"type": "leaf",
"state": {
"type": "release-notes",
"state": {
"currentVersion": "1.8.4"
},
"icon": "lucide-book-up",
"title": "Release Notes 1.8.4"
} }
] }
],
"currentTab": 1
} }
], ],
"direction": "vertical" "direction": "vertical"
@@ -40,7 +53,8 @@
"state": { "state": {
"type": "file-explorer", "type": "file-explorer",
"state": { "state": {
"sortOrder": "alphabetical" "sortOrder": "alphabetical",
"autoReveal": false
}, },
"icon": "lucide-folder-closed", "icon": "lucide-folder-closed",
"title": "Files" "title": "Files"
@@ -162,7 +176,7 @@
"command-palette:Open command palette": false "command-palette:Open command palette": false
} }
}, },
"active": "1eda716d35acafe8", "active": "7899dc85e8880db2",
"lastOpenFiles": [ "lastOpenFiles": [
"index.md", "index.md",
"DeepImpact.md", "DeepImpact.md",

BIN
docs/Nullframe Discord.pdf Normal file

Binary file not shown.

View File

@@ -12,3 +12,6 @@ Here you'll find documentation for various tools and projects I've created. Sele
If you need help or have questions: If you need help or have questions:
- Open an issue on [GitHub](https://github.com/Litruv) - Open an issue on [GitHub](https://github.com/Litruv)
- Join the discussion on [Discord](https://discord.gg/b8ctQBaaax) - Join the discussion on [Discord](https://discord.gg/b8ctQBaaax)
asdfds

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Documentation Viewer</title> <title>Litruv</title>
<meta name="description" content="Documentation for Litruv's tools and projects"> <meta name="description" content="Documentation for Litruv's tools and projects">
<meta property="og:type" content="article"> <meta property="og:type" content="article">
@@ -12,44 +12,10 @@
<meta name="twitter:card" content="summary"> <meta name="twitter:card" content="summary">
<meta name="twitter:description" content="Documentation for Litruv's tools and projects"> <meta name="twitter:description" content="Documentation for Litruv's tools and projects">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-tomorrow.min.css">
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github-dark.min.css">
<script>
function getRandomString(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
function addRandomMetaTags() {
const ogTitle = document.createElement('meta');
ogTitle.setAttribute('property', 'og:title');
ogTitle.content = getRandomString(10);
document.head.appendChild(ogTitle);
const ogImage = document.createElement('meta');
ogImage.setAttribute('property', 'og:image');
ogImage.content = `https://example.com/${getRandomString(10)}.jpg`;
document.head.appendChild(ogImage);
const twitterTitle = document.createElement('meta');
twitterTitle.setAttribute('name', 'twitter:title');
twitterTitle.content = getRandomString(10);
document.head.appendChild(twitterTitle);
const twitterImage = document.createElement('meta');
twitterImage.setAttribute('name', 'twitter:image');
twitterImage.content = `https://example.com/${getRandomString(10)}.jpg`;
document.head.appendChild(twitterImage);
}
document.addEventListener('DOMContentLoaded', addRandomMetaTags);
</script>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
@@ -103,17 +69,10 @@
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/marked@4.3.0/marked.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked@4.3.0/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script> <script>
window.addEventListener('error', function(e) { hljs.highlightAll();
if (e.target.src && e.target.src.includes('marked.min.js')) {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js';
document.body.appendChild(script);
}
}, true);
</script> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="index.js"></script> <script src="index.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,3 +1,5 @@
let originalDocTitle = document.title;
let markedPromise = new Promise((resolve) => { let markedPromise = new Promise((resolve) => {
if (typeof marked !== 'undefined') { if (typeof marked !== 'undefined') {
resolve(marked); resolve(marked);
@@ -12,6 +14,18 @@ async function initializeMarked() {
const renderer = new marked.Renderer(); const renderer = new marked.Renderer();
const originalLink = renderer.link.bind(renderer); const originalLink = renderer.link.bind(renderer);
renderer.code = (code, language) => {
let highlighted;
if (language && hljs.getLanguage(language)) {
highlighted = hljs.highlight(code, { language }).value;
language = language;
} else {
highlighted = hljs.highlightAuto(code).value;
language = '';
}
return `<pre class="hljs ${language ? "language-" + language : ""}"><code>${highlighted}</code></pre>`;
};
renderer.link = (href, title, text) => { renderer.link = (href, title, text) => {
const isExternal = href.startsWith('http') || href.startsWith('https'); const isExternal = href.startsWith('http') || href.startsWith('https');
const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : ''; const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
@@ -23,12 +37,6 @@ async function initializeMarked() {
}; };
marked.setOptions({ marked.setOptions({
highlight: function(code, lang) {
if (Prism.languages[lang]) {
return Prism.highlight(code, Prism.languages[lang], lang);
}
return code;
},
breaks: true, breaks: true,
gfm: true, gfm: true,
renderer: renderer renderer: renderer
@@ -250,6 +258,9 @@ async function loadDocument(path) {
const titleContent = metadata.title || (docEntry && docEntry.title) || path.split('/').pop().replace('.md', ''); const titleContent = metadata.title || (docEntry && docEntry.title) || path.split('/').pop().replace('.md', '');
document.title = `${originalDocTitle} / ${titleContent}`;
document.querySelector('.title-text .page-title').textContent = titleContent;
let processedContent = content.replace(/\[\[(.*?)\]\]/g, (match, linkText) => { let processedContent = content.replace(/\[\[(.*?)\]\]/g, (match, linkText) => {
if (linkText.match(/\.(png|jpg|jpeg|gif|mp4|webm)$/i)) { if (linkText.match(/\.(png|jpg|jpeg|gif|mp4|webm)$/i)) {
return match; return match;
@@ -277,25 +288,11 @@ async function loadDocument(path) {
return `\n![${filename}](${mediaPath})\n\n`; return `\n![${filename}](${mediaPath})\n\n`;
}); });
document.title = `Litruv / ${titleContent}`;
document.querySelector('.title-text .page-title').textContent = titleContent; document.querySelector('.title-text .page-title').textContent = titleContent;
// Update Twitter meta tags dynamically
document.querySelector('meta[name="twitter:title"]').setAttribute('content', titleContent);
document.querySelector('meta[name="twitter:description"]').setAttribute('content', metadata.description || `Documentation for ${titleContent}`);
if (metadata.image) {
const imageUrl = `${window.location.origin}${window.location.pathname}${basePath}/images/${metadata.image}`;
let twitterImageMeta = document.querySelector('meta[name="twitter:image"]');
if (!twitterImageMeta) {
twitterImageMeta = document.createElement('meta');
twitterImageMeta.setAttribute('name', 'twitter:image');
document.head.appendChild(twitterImageMeta);
}
twitterImageMeta.setAttribute('content', imageUrl);
}
documentContent.className = 'markdown-content'; documentContent.className = 'markdown-content';
documentContent.innerHTML = marked.parse(finalContent); documentContent.innerHTML = marked.parse(finalContent);
hljs.highlightAll();
documentOutline.innerHTML = ''; documentOutline.innerHTML = '';
const headings = documentContent.querySelectorAll('h1, h2, h3, h4, h5, h6'); const headings = documentContent.querySelectorAll('h1, h2, h3, h4, h5, h6');

View File

@@ -421,6 +421,112 @@ social-links a:hover {
border-bottom: 1px solid var(--ifm-border-color); border-bottom: 1px solid var(--ifm-border-color);
} }
.markdown-content pre {
background-color: var(--ifm-background-surface-color);
border: 1px solid var(--ifm-border-color);
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
overflow-x: auto;
}
.markdown-content pre code {
background: none;
border: none;
padding: 0;
font-size: 0.9rem;
line-height: 1.5;
color: inherit;
}
/* Override Prism styles to match theme */
pre[class*="language-"] {
background: var(--ifm-background-surface-color) !important;
margin: 1rem 0 !important;
padding: 1rem !important;
border-radius: 8px !important;
}
code[class*="language-"] {
background: none !important;
text-shadow: none !important;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
}
/* Code block styles */
.markdown-content pre[class*="language-"] {
background: var(--ifm-background-surface-color) !important;
border: 1px solid var(--ifm-border-color);
margin: 1.5rem 0;
padding: 1rem;
border-radius: 8px;
}
.markdown-content code[class*="language-"] {
background: none;
padding: 0;
margin: 0;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.9em;
line-height: 1.5;
text-shadow: none;
}
/* Discord-like code block styles */
pre[class*="language-"] {
background-color: #2f3136 !important;
color: #b9bbbe !important;
border: none;
padding: 1rem !important;
border-radius: 5px !important;
overflow-x: auto;
line-height: 1.5;
}
code[class*="language-"] {
background: none !important;
color: inherit !important;
font-family: 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
}
/* Optionally, if you use Prism's toolbar, adjust its colors similar to Discord */
div.code-toolbar > .toolbar > .toolbar-item > button {
background: #2f3136;
color: #b9bbbe;
border: none;
}
div.code-toolbar > .toolbar > .toolbar-item > button:hover {
background: #40444b;
color: #dcddde;
}
/* Toolbar styles */
div.code-toolbar > .toolbar {
opacity: 0;
top: 0.5em;
right: 0.5em;
transition: opacity 0.3s;
}
div.code-toolbar:hover > .toolbar {
opacity: 1;
}
div.code-toolbar > .toolbar > .toolbar-item > button {
background: var(--ifm-background-surface-color);
color: var(--ifm-color-content);
border: 1px solid var(--ifm-border-color);
border-radius: 4px;
padding: 0.4em 0.8em;
font-size: 0.8em;
cursor: pointer;
}
div.code-toolbar > .toolbar > .toolbar-item > button:hover {
background: var(--ifm-color-primary);
color: white;
}
.error { .error {
color: #ff6b6b; color: #ff6b6b;
padding: 20px; padding: 20px;