mirror of
https://github.com/litruv/Docs-Viewer.git
synced 2026-07-24 10:46:09 +10:00
Add initial configuration files and update documentation links
This commit is contained in:
1
docs/.obsidian/app.json
vendored
Normal file
1
docs/.obsidian/app.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
docs/.obsidian/appearance.json
vendored
Normal file
1
docs/.obsidian/appearance.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
30
docs/.obsidian/core-plugins.json
vendored
Normal file
30
docs/.obsidian/core-plugins.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"file-explorer": true,
|
||||||
|
"global-search": true,
|
||||||
|
"switcher": true,
|
||||||
|
"graph": true,
|
||||||
|
"backlink": true,
|
||||||
|
"canvas": true,
|
||||||
|
"outgoing-link": true,
|
||||||
|
"tag-pane": true,
|
||||||
|
"properties": false,
|
||||||
|
"page-preview": true,
|
||||||
|
"daily-notes": true,
|
||||||
|
"templates": true,
|
||||||
|
"note-composer": true,
|
||||||
|
"command-palette": true,
|
||||||
|
"slash-command": false,
|
||||||
|
"editor-status": true,
|
||||||
|
"bookmarks": true,
|
||||||
|
"markdown-importer": false,
|
||||||
|
"zk-prefixer": false,
|
||||||
|
"random-note": false,
|
||||||
|
"outline": true,
|
||||||
|
"word-count": true,
|
||||||
|
"slides": false,
|
||||||
|
"audio-recorder": false,
|
||||||
|
"workspaces": false,
|
||||||
|
"file-recovery": true,
|
||||||
|
"publish": false,
|
||||||
|
"sync": false
|
||||||
|
}
|
||||||
171
docs/.obsidian/workspace.json
vendored
Normal file
171
docs/.obsidian/workspace.json
vendored
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
{
|
||||||
|
"main": {
|
||||||
|
"id": "cd6401f1d421f524",
|
||||||
|
"type": "split",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "380ab91822574142",
|
||||||
|
"type": "tabs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "1eda716d35acafe8",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "markdown",
|
||||||
|
"state": {
|
||||||
|
"file": "index.md",
|
||||||
|
"mode": "source",
|
||||||
|
"source": false
|
||||||
|
},
|
||||||
|
"icon": "lucide-file",
|
||||||
|
"title": "index"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"direction": "vertical"
|
||||||
|
},
|
||||||
|
"left": {
|
||||||
|
"id": "66837e31272b0a87",
|
||||||
|
"type": "split",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "2c0fd441985c6245",
|
||||||
|
"type": "tabs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "ba3b4ab7076efb70",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "file-explorer",
|
||||||
|
"state": {
|
||||||
|
"sortOrder": "alphabetical"
|
||||||
|
},
|
||||||
|
"icon": "lucide-folder-closed",
|
||||||
|
"title": "Files"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "a6d0910ec51944ee",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "search",
|
||||||
|
"state": {
|
||||||
|
"query": "",
|
||||||
|
"matchingCase": false,
|
||||||
|
"explainSearch": false,
|
||||||
|
"collapseAll": false,
|
||||||
|
"extraContext": false,
|
||||||
|
"sortOrder": "alphabetical"
|
||||||
|
},
|
||||||
|
"icon": "lucide-search",
|
||||||
|
"title": "Search"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f8024f6e5f6c45ac",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "bookmarks",
|
||||||
|
"state": {},
|
||||||
|
"icon": "lucide-bookmark",
|
||||||
|
"title": "Bookmarks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"direction": "horizontal",
|
||||||
|
"width": 300
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"id": "642d5319f1f437db",
|
||||||
|
"type": "split",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "6c06163fdc608e38",
|
||||||
|
"type": "tabs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "b06989fdae9db98b",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "backlink",
|
||||||
|
"state": {
|
||||||
|
"file": "index.md",
|
||||||
|
"collapseAll": false,
|
||||||
|
"extraContext": false,
|
||||||
|
"sortOrder": "alphabetical",
|
||||||
|
"showSearch": false,
|
||||||
|
"searchQuery": "",
|
||||||
|
"backlinkCollapsed": false,
|
||||||
|
"unlinkedCollapsed": true
|
||||||
|
},
|
||||||
|
"icon": "links-coming-in",
|
||||||
|
"title": "Backlinks for index"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bf67716e89e5fce6",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "outgoing-link",
|
||||||
|
"state": {
|
||||||
|
"file": "index.md",
|
||||||
|
"linksCollapsed": false,
|
||||||
|
"unlinkedCollapsed": true
|
||||||
|
},
|
||||||
|
"icon": "links-going-out",
|
||||||
|
"title": "Outgoing links from index"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ad8500c55403fa87",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "tag",
|
||||||
|
"state": {
|
||||||
|
"sortOrder": "frequency",
|
||||||
|
"useHierarchy": true
|
||||||
|
},
|
||||||
|
"icon": "lucide-tags",
|
||||||
|
"title": "Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b67ceee19888e290",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "outline",
|
||||||
|
"state": {
|
||||||
|
"file": "index.md"
|
||||||
|
},
|
||||||
|
"icon": "lucide-list",
|
||||||
|
"title": "Outline of index"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"direction": "horizontal",
|
||||||
|
"width": 300,
|
||||||
|
"collapsed": true
|
||||||
|
},
|
||||||
|
"left-ribbon": {
|
||||||
|
"hiddenItems": {
|
||||||
|
"switcher:Open quick switcher": false,
|
||||||
|
"graph:Open graph view": false,
|
||||||
|
"canvas:Create new canvas": false,
|
||||||
|
"daily-notes:Open today's daily note": false,
|
||||||
|
"templates:Insert template": false,
|
||||||
|
"command-palette:Open command palette": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": "1eda716d35acafe8",
|
||||||
|
"lastOpenFiles": [
|
||||||
|
"Blueprint Penetration Trace.md",
|
||||||
|
"Blueprint Depth Trace.md",
|
||||||
|
"index.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
---
|
|
||||||
title: Welcome
|
|
||||||
---
|
|
||||||
Welcome to the Documentation
|
Welcome to the Documentation
|
||||||
|
|
||||||
Here you'll find documentation for various tools and projects I've created. Select a document from the sidebar to get started.
|
Here you'll find documentation for various tools and projects I've created. Select a document from the sidebar to get started.
|
||||||
|
|
||||||
## Available Documentation
|
## Available Documentation
|
||||||
|
|
||||||
- **Blueprint Depth Trace** - A Blueprint Function Library for depth-based line traces in Unreal Engine
|
- **[[Blueprint Depth Trace]]** - A Blueprint Function Library for depth-based line traces in Unreal Engine
|
||||||
- More documentation coming soon...
|
- More documentation coming soon...
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|||||||
131
index.js
131
index.js
@@ -1,4 +1,3 @@
|
|||||||
// Initialize marked when it's available
|
|
||||||
let markedPromise = new Promise((resolve) => {
|
let markedPromise = new Promise((resolve) => {
|
||||||
if (typeof marked !== 'undefined') {
|
if (typeof marked !== 'undefined') {
|
||||||
resolve(marked);
|
resolve(marked);
|
||||||
@@ -7,9 +6,24 @@ let markedPromise = new Promise((resolve) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure marked options
|
|
||||||
async function initializeMarked() {
|
async function initializeMarked() {
|
||||||
const marked = await markedPromise;
|
const marked = await markedPromise;
|
||||||
|
|
||||||
|
// Create a new renderer
|
||||||
|
const renderer = new marked.Renderer();
|
||||||
|
|
||||||
|
// Store the original link renderer
|
||||||
|
const originalLink = renderer.link.bind(renderer);
|
||||||
|
|
||||||
|
// Override the link renderer
|
||||||
|
renderer.link = (href, title, text) => {
|
||||||
|
const isExternal = href.startsWith('http') || href.startsWith('https');
|
||||||
|
const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
|
||||||
|
const link = originalLink(href, title, text);
|
||||||
|
return link.replace(/^<a /, `<a${attrs} `);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set options with the custom renderer
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
highlight: function(code, lang) {
|
highlight: function(code, lang) {
|
||||||
if (Prism.languages[lang]) {
|
if (Prism.languages[lang]) {
|
||||||
@@ -18,8 +32,10 @@ async function initializeMarked() {
|
|||||||
return code;
|
return code;
|
||||||
},
|
},
|
||||||
breaks: true,
|
breaks: true,
|
||||||
gfm: true
|
gfm: true,
|
||||||
|
renderer: renderer
|
||||||
});
|
});
|
||||||
|
|
||||||
return marked;
|
return marked;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +43,7 @@ function createFileIndexItem(doc, container, level = 0) {
|
|||||||
if (doc.type === 'folder') {
|
if (doc.type === 'folder') {
|
||||||
const folderDiv = document.createElement('div');
|
const folderDiv = document.createElement('div');
|
||||||
folderDiv.className = 'folder open';
|
folderDiv.className = 'folder open';
|
||||||
folderDiv.dataset.path = doc.title; // Add data attribute for folder identification
|
folderDiv.dataset.path = doc.title;
|
||||||
folderDiv.style.paddingLeft = `${level * 0.8}rem`;
|
folderDiv.style.paddingLeft = `${level * 0.8}rem`;
|
||||||
|
|
||||||
const folderHeader = document.createElement('div');
|
const folderHeader = document.createElement('div');
|
||||||
@@ -52,8 +68,10 @@ function createFileIndexItem(doc, container, level = 0) {
|
|||||||
container.appendChild(folderDiv);
|
container.appendChild(folderDiv);
|
||||||
} else {
|
} else {
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = `?${doc.slug}`; // Use slug instead of generating filename
|
link.href = `?${doc.slug}`;
|
||||||
link.textContent = doc.title;
|
// Use title from json or fallback to filename
|
||||||
|
const displayTitle = doc.title || doc.path.split('/').pop().replace('.md', '');
|
||||||
|
link.textContent = displayTitle;
|
||||||
link.dataset.path = doc.path;
|
link.dataset.path = doc.path;
|
||||||
link.style.paddingLeft = `${level * 0.8 + 1.2}rem`;
|
link.style.paddingLeft = `${level * 0.8 + 1.2}rem`;
|
||||||
|
|
||||||
@@ -66,11 +84,9 @@ function createFileIndexItem(doc, container, level = 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add this new function to find parent folders of a path
|
|
||||||
function findParentFolders(documents, path, parentFolders = []) {
|
function findParentFolders(documents, path, parentFolders = []) {
|
||||||
for (const doc of documents) {
|
for (const doc of documents) {
|
||||||
if (doc.type === 'folder') {
|
if (doc.type === 'folder') {
|
||||||
// Check if path is in this folder
|
|
||||||
const found = doc.items.find(item => {
|
const found = doc.items.find(item => {
|
||||||
if (item.path === path) return true;
|
if (item.path === path) return true;
|
||||||
if (item.type === 'folder') {
|
if (item.type === 'folder') {
|
||||||
@@ -81,7 +97,6 @@ function findParentFolders(documents, path, parentFolders = []) {
|
|||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
parentFolders.push(doc);
|
parentFolders.push(doc);
|
||||||
// Continue searching in case of nested folders
|
|
||||||
doc.items.forEach(item => {
|
doc.items.forEach(item => {
|
||||||
if (item.type === 'folder') {
|
if (item.type === 'folder') {
|
||||||
findParentFolders([item], path, parentFolders);
|
findParentFolders([item], path, parentFolders);
|
||||||
@@ -93,7 +108,6 @@ function findParentFolders(documents, path, parentFolders = []) {
|
|||||||
return parentFolders;
|
return parentFolders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to find document by slug in nested structure
|
|
||||||
function findDocumentBySlug(documents, slug) {
|
function findDocumentBySlug(documents, slug) {
|
||||||
for (const doc of documents) {
|
for (const doc of documents) {
|
||||||
if (doc.type === 'folder') {
|
if (doc.type === 'folder') {
|
||||||
@@ -112,28 +126,24 @@ async function loadIndex() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const fileIndex = document.getElementById('file-index');
|
const fileIndex = document.getElementById('file-index');
|
||||||
|
|
||||||
// Clear existing content
|
|
||||||
fileIndex.innerHTML = '';
|
fileIndex.innerHTML = '';
|
||||||
|
|
||||||
// Build file index with folder support
|
|
||||||
data.documents.forEach(doc => createFileIndexItem(doc, fileIndex));
|
data.documents.forEach(doc => createFileIndexItem(doc, fileIndex));
|
||||||
|
|
||||||
// Handle initial URL load or load index.md by default
|
|
||||||
const queryString = window.location.search;
|
const queryString = window.location.search;
|
||||||
if (queryString) {
|
if (queryString) {
|
||||||
const slug = queryString.substring(1); // Remove the leading '?'
|
const slug = queryString.substring(1);
|
||||||
console.log('Loading document for slug:', slug); // Debug log
|
console.log('Loading document for slug:', slug);
|
||||||
|
|
||||||
const matchingDoc = findDocumentBySlug(data.documents, slug);
|
const matchingDoc = findDocumentBySlug(data.documents, slug);
|
||||||
|
|
||||||
if (matchingDoc) {
|
if (matchingDoc) {
|
||||||
console.log('Found matching document:', matchingDoc); // Debug log
|
console.log('Found matching document:', matchingDoc);
|
||||||
await loadDocument(matchingDoc.path);
|
await loadDocument(matchingDoc.path);
|
||||||
} else {
|
} else {
|
||||||
console.warn('No matching document found for slug:', slug); // Debug log
|
console.warn('No matching document found for slug:', slug);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Load index.md by default when no document is specified
|
|
||||||
const defaultDoc = findDocumentBySlug(data.documents, 'welcome');
|
const defaultDoc = findDocumentBySlug(data.documents, 'welcome');
|
||||||
if (defaultDoc) {
|
if (defaultDoc) {
|
||||||
await loadDocument(defaultDoc.path);
|
await loadDocument(defaultDoc.path);
|
||||||
@@ -146,7 +156,6 @@ async function loadIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add popstate handler for browser back/forward buttons
|
|
||||||
window.addEventListener('popstate', async () => {
|
window.addEventListener('popstate', async () => {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const slug = urlParams.toString().replace(/^=/, '').replace(/^\?/, '');
|
const slug = urlParams.toString().replace(/^=/, '').replace(/^\?/, '');
|
||||||
@@ -165,17 +174,14 @@ function extractMetadata(content) {
|
|||||||
let metadata = {};
|
let metadata = {};
|
||||||
let contentStart = 0;
|
let contentStart = 0;
|
||||||
|
|
||||||
// Check if the file starts with a metadata section (accounting for whitespace)
|
|
||||||
if (lines[0].trim() === '---') {
|
if (lines[0].trim() === '---') {
|
||||||
let endMetadata = -1;
|
let endMetadata = -1;
|
||||||
|
|
||||||
// Find the closing '---'
|
|
||||||
for (let i = 1; i < lines.length; i++) {
|
for (let i = 1; i < lines.length; i++) {
|
||||||
if (lines[i].trim() === '---') {
|
if (lines[i].trim() === '---') {
|
||||||
endMetadata = i;
|
endMetadata = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Parse key-value pairs (ignore empty lines)
|
|
||||||
const line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
if (line) {
|
if (line) {
|
||||||
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
||||||
@@ -190,27 +196,39 @@ function extractMetadata(content) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim any leading/trailing whitespace from the content
|
|
||||||
return {
|
return {
|
||||||
metadata,
|
metadata,
|
||||||
content: lines.slice(contentStart).join('\n').trim()
|
content: lines.slice(contentStart).join('\n').trim()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findDocumentByTitle(documents, title) {
|
||||||
|
for (const doc of documents) {
|
||||||
|
if (doc.type === 'folder') {
|
||||||
|
const found = findDocumentByTitle(doc.items, title);
|
||||||
|
if (found) return found;
|
||||||
|
} else if (doc.title === title || doc.path.endsWith(title + '.md')) {
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadDocument(path) {
|
async function loadDocument(path) {
|
||||||
try {
|
try {
|
||||||
// Update active state in sidebar and expand folders
|
const documentContent = document.getElementById('document-content');
|
||||||
|
const documentOutline = document.getElementById('document-outline');
|
||||||
|
const basePath = path.substring(0, path.lastIndexOf('/'));
|
||||||
|
|
||||||
document.querySelectorAll('#file-index a').forEach(link => {
|
document.querySelectorAll('#file-index a').forEach(link => {
|
||||||
link.classList.toggle('active', link.dataset.path === path);
|
link.classList.toggle('active', link.dataset.path === path);
|
||||||
|
|
||||||
// If this is the active link, expand its parent folders
|
|
||||||
if (link.dataset.path === path) {
|
if (link.dataset.path === path) {
|
||||||
const response = fetch('index.json')
|
const response = fetch('index.json')
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const parentFolders = findParentFolders(data.documents, path);
|
const parentFolders = findParentFolders(data.documents, path);
|
||||||
parentFolders.forEach(folder => {
|
parentFolders.forEach(folder => {
|
||||||
// Find and expand the corresponding folder div
|
|
||||||
const folderDiv = document.querySelector(`.folder[data-path="${folder.title}"]`);
|
const folderDiv = document.querySelector(`.folder[data-path="${folder.title}"]`);
|
||||||
if (folderDiv) {
|
if (folderDiv) {
|
||||||
folderDiv.classList.add('open');
|
folderDiv.classList.add('open');
|
||||||
@@ -231,19 +249,34 @@ async function loadDocument(path) {
|
|||||||
let rawContent = await response.text();
|
let rawContent = await response.text();
|
||||||
const { metadata, content } = extractMetadata(rawContent);
|
const { metadata, content } = extractMetadata(rawContent);
|
||||||
|
|
||||||
const basePath = path.substring(0, path.lastIndexOf('/'));
|
// Get the title from index.json
|
||||||
const documentContent = document.getElementById('document-content');
|
const indexResponse = await fetch('index.json');
|
||||||
const documentOutline = document.getElementById('document-outline');
|
const indexData = await indexResponse.json();
|
||||||
|
const docEntry = findDocumentByPath(indexData.documents, path);
|
||||||
|
|
||||||
// Create title element
|
// Use title priority: frontmatter > json > filename
|
||||||
const titleContent = metadata.title || path.split('/').pop().replace('.md', '');
|
const titleContent = metadata.title || (docEntry && docEntry.title) || path.split('/').pop().replace('.md', '');
|
||||||
const processedContent = `# ${titleContent}\n\n${content}`;
|
|
||||||
|
|
||||||
// Convert Obsidian media links to HTML with proper spacing
|
// Process Obsidian internal links before adding the title
|
||||||
|
let processedContent = content.replace(/\[\[(.*?)\]\]/g, (match, linkText) => {
|
||||||
|
// Skip image/media processing
|
||||||
|
if (linkText.match(/\.(png|jpg|jpeg|gif|mp4|webm)$/i)) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = findDocumentByTitle(indexData.documents, linkText);
|
||||||
|
if (doc) {
|
||||||
|
return `[${doc.title}](?${doc.slug})`;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
processedContent = `# ${titleContent}\n\n${processedContent}`;
|
||||||
|
|
||||||
|
// Process images/media after internal links
|
||||||
const finalContent = processedContent.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
|
const finalContent = processedContent.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
|
||||||
const mediaPath = `${basePath}/images/${filename}`;
|
const mediaPath = `${basePath}/images/${filename}`;
|
||||||
|
|
||||||
// Check if it's a video file
|
|
||||||
if (filename.toLowerCase().endsWith('.mp4')) {
|
if (filename.toLowerCase().endsWith('.mp4')) {
|
||||||
return `\n<video controls width="100%">
|
return `\n<video controls width="100%">
|
||||||
<source src="${mediaPath}" type="video/mp4">
|
<source src="${mediaPath}" type="video/mp4">
|
||||||
@@ -251,11 +284,9 @@ async function loadDocument(path) {
|
|||||||
</video>\n\n`;
|
</video>\n\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to image handling with added newlines
|
|
||||||
return `\n\n\n`;
|
return `\n\n\n`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update meta tags
|
|
||||||
const description = metadata.description || `Documentation for ${titleContent}`;
|
const description = metadata.description || `Documentation for ${titleContent}`;
|
||||||
const url = `${window.location.origin}${window.location.pathname}${window.location.search}`;
|
const url = `${window.location.origin}${window.location.pathname}${window.location.search}`;
|
||||||
|
|
||||||
@@ -267,7 +298,6 @@ async function loadDocument(path) {
|
|||||||
document.querySelector('meta[name="twitter:title"]').setAttribute('content', titleContent);
|
document.querySelector('meta[name="twitter:title"]').setAttribute('content', titleContent);
|
||||||
document.querySelector('meta[name="twitter:description"]').setAttribute('content', description);
|
document.querySelector('meta[name="twitter:description"]').setAttribute('content', description);
|
||||||
|
|
||||||
// If there's a cover image in metadata
|
|
||||||
if (metadata.image) {
|
if (metadata.image) {
|
||||||
const imageUrl = `${window.location.origin}${window.location.pathname}${basePath}/images/${metadata.image}`;
|
const imageUrl = `${window.location.origin}${window.location.pathname}${basePath}/images/${metadata.image}`;
|
||||||
document.querySelector('meta[property="og:image"]')?.remove();
|
document.querySelector('meta[property="og:image"]')?.remove();
|
||||||
@@ -284,15 +314,12 @@ async function loadDocument(path) {
|
|||||||
document.head.appendChild(twitterImage);
|
document.head.appendChild(twitterImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update page and title bar while preserving menu button and brand
|
|
||||||
document.title = `Litruv / ${titleContent}`;
|
document.title = `Litruv / ${titleContent}`;
|
||||||
document.querySelector('.title-text .page-title').textContent = titleContent;
|
document.querySelector('.title-text .page-title').textContent = titleContent;
|
||||||
|
|
||||||
// Parse markdown content
|
|
||||||
documentContent.className = 'markdown-content';
|
documentContent.className = 'markdown-content';
|
||||||
documentContent.innerHTML = marked.parse(finalContent);
|
documentContent.innerHTML = marked.parse(finalContent);
|
||||||
|
|
||||||
// Generate outline
|
|
||||||
documentOutline.innerHTML = '';
|
documentOutline.innerHTML = '';
|
||||||
const headings = documentContent.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
const headings = documentContent.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||||
|
|
||||||
@@ -310,25 +337,20 @@ async function loadDocument(path) {
|
|||||||
|
|
||||||
link.onclick = (e) => {
|
link.onclick = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Update URL without triggering navigation
|
|
||||||
history.pushState(null, '', link.href);
|
history.pushState(null, '', link.href);
|
||||||
|
|
||||||
// Remove existing highlights
|
|
||||||
document.querySelectorAll('.highlight').forEach(el => {
|
document.querySelectorAll('.highlight').forEach(el => {
|
||||||
el.classList.remove('highlight');
|
el.classList.remove('highlight');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add highlight class to trigger animation
|
|
||||||
heading.classList.add('highlight');
|
heading.classList.add('highlight');
|
||||||
|
|
||||||
// Smooth scroll
|
|
||||||
heading.scrollIntoView({ behavior: 'smooth' });
|
heading.scrollIntoView({ behavior: 'smooth' });
|
||||||
};
|
};
|
||||||
|
|
||||||
documentOutline.appendChild(link);
|
documentOutline.appendChild(link);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check for hash in URL and highlight on load
|
|
||||||
if (window.location.hash) {
|
if (window.location.hash) {
|
||||||
const id = window.location.hash.substring(1);
|
const id = window.location.hash.substring(1);
|
||||||
const heading = document.getElementById(id);
|
const heading = document.getElementById(id);
|
||||||
@@ -338,7 +360,6 @@ async function loadDocument(path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight code blocks
|
|
||||||
Prism.highlightAll();
|
Prism.highlightAll();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading document:', error);
|
console.error('Error loading document:', error);
|
||||||
@@ -347,7 +368,18 @@ async function loadDocument(path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle image paths
|
function findDocumentByPath(documents, path) {
|
||||||
|
for (const doc of documents) {
|
||||||
|
if (doc.type === 'folder') {
|
||||||
|
const found = findDocumentByPath(doc.items, path);
|
||||||
|
if (found) return found;
|
||||||
|
} else if (doc.path === path) {
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function fixImagePaths(basePath, content) {
|
function fixImagePaths(basePath, content) {
|
||||||
return content.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
|
return content.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
|
||||||
const imagePath = basePath + '/images/' + filename;
|
const imagePath = basePath + '/images/' + filename;
|
||||||
@@ -355,7 +387,6 @@ function fixImagePaths(basePath, content) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add menu toggle functionality
|
|
||||||
function setupMobileMenu() {
|
function setupMobileMenu() {
|
||||||
const menuButton = document.querySelector('.menu-button');
|
const menuButton = document.querySelector('.menu-button');
|
||||||
const leftSidebar = document.querySelector('.left-sidebar');
|
const leftSidebar = document.querySelector('.left-sidebar');
|
||||||
@@ -365,7 +396,6 @@ function setupMobileMenu() {
|
|||||||
leftSidebar.classList.toggle('show');
|
leftSidebar.classList.toggle('show');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close menu when clicking outside
|
|
||||||
content.addEventListener('click', () => {
|
content.addEventListener('click', () => {
|
||||||
if (leftSidebar.classList.contains('show')) {
|
if (leftSidebar.classList.contains('show')) {
|
||||||
leftSidebar.classList.remove('show');
|
leftSidebar.classList.remove('show');
|
||||||
@@ -373,9 +403,8 @@ function setupMobileMenu() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new initialization with proper order
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
console.log('Window loaded, initializing...'); // Debug log
|
console.log('Window loaded, initializing...');
|
||||||
setupMobileMenu();
|
setupMobileMenu();
|
||||||
loadIndex().catch(error => {
|
loadIndex().catch(error => {
|
||||||
console.error('Failed to initialize:', error);
|
console.error('Failed to initialize:', error);
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"title": "Blueprint Depth Trace",
|
"title": "Blueprint Depth Trace",
|
||||||
"path": "docs/Blueprint Penetration Trace.md",
|
"path": "docs/Blueprint Depth Trace.md",
|
||||||
"slug": "blueprint-penetration-trace"
|
"slug": "blueprint-depth-trace"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
23
styles.css
23
styles.css
@@ -74,11 +74,15 @@ body {
|
|||||||
border-right: 1px solid var(--ifm-border-color);
|
border-right: 1px solid var(--ifm-border-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-height: calc(100vh - var(--title-bar-height));
|
||||||
|
position: sticky;
|
||||||
|
top: var(--title-bar-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
#file-index {
|
#file-index {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
min-height: 0; /* This ensures the flex item can shrink */
|
||||||
}
|
}
|
||||||
|
|
||||||
#file-index a {
|
#file-index a {
|
||||||
@@ -139,11 +143,11 @@ body {
|
|||||||
|
|
||||||
.social-links {
|
.social-links {
|
||||||
padding: 1rem 1rem 0.5rem;
|
padding: 1rem 1rem 0.5rem;
|
||||||
border-top: 1px solid var(--ifm-border-color);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
background: var(--ifm-background-surface-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.social-links a {
|
.social-links a {
|
||||||
@@ -157,11 +161,13 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
|
margin-top: auto; /* Push to bottom of flex container */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0 1rem 1rem;
|
padding: 0 1rem 1rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: var(--ifm-color-content-secondary);
|
color: var(--ifm-color-content-secondary);
|
||||||
border-top: none;
|
border-top: 1px solid var(--ifm-border-color);
|
||||||
|
background: var(--ifm-background-surface-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle .name {
|
.subtitle .name {
|
||||||
@@ -203,16 +209,15 @@ body {
|
|||||||
color: var(--ifm-color-primary);
|
color: var(--ifm-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#document-outline {
|
||||||
|
padding: 0 1rem; /* Add horizontal padding */
|
||||||
|
}
|
||||||
|
|
||||||
#document-outline a {
|
#document-outline a {
|
||||||
color: var(--ifm-color-content-secondary);
|
color: var(--ifm-color-content-secondary);
|
||||||
border-left: 2px solid transparent;
|
border-left: 2px solid transparent;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
}
|
padding: 0.4rem 1rem; /* Adjust padding to match */
|
||||||
|
|
||||||
#document-outline a:hover {
|
|
||||||
background-color: transparent;
|
|
||||||
color: var(--ifm-color-primary);
|
|
||||||
border-left: 2px solid var(--ifm-color-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Markdown content styling */
|
/* Markdown content styling */
|
||||||
@@ -413,6 +418,8 @@ body {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
transition: var(--sidebar-transition);
|
transition: var(--sidebar-transition);
|
||||||
|
min-height: unset; /* Remove min-height for mobile */
|
||||||
|
height: calc(100% - var(--title-bar-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
|
|||||||
Reference in New Issue
Block a user