Knowledge base: /docs/<title> permalinks, search, categories, count, better PDF
Articles get their own address (shareable, back/forward work); the list is searchable by title and text, shows the article count, and is grouped into Applications and Troubleshooting & Guides via a hidden category line set from the editor. PDF export lays out at page width with page breaks, a title block and page numbers; Print uses the browser's vector print. Inline code is a pill that glows blue on hover, blockquotes no longer squeeze, and GitHub-style [!NOTE] alerts render. [[Badge]] syntax inside code spans is left alone.
This commit is contained in:
1 parent
514bc31566
commit
a5c0a5210e
24 files changed
+525
-11
No files matched your search
+24
-10
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>USMT Dash</title>
|
||||
<base href="/">
|
||||
<link rel="icon" href="/favicon.png" type="image/png">
|
||||
|
||||
<script src="/vendor/marked/marked.min.js"></script>
|
||||
@@ -353,9 +354,11 @@
|
||||
</style>
|
||||
<link rel="stylesheet" href="/layout.css">
|
||||
<link rel="stylesheet" href="/overview.css">
|
||||
<link rel="stylesheet" href="/docs.css">
|
||||
<script src="/layout.js"></script>
|
||||
<script src="/device-info.js"></script>
|
||||
<script src="/overview.js"></script>
|
||||
<script src="/docs.js"></script>
|
||||
<script src="/excel-filter.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1751,7 +1754,7 @@
|
||||
const preview = document.getElementById('docContent');
|
||||
if (preview && preview.style.display !== 'none') {
|
||||
// Encode spaces ONLY in UNC/Local paths so marked.js doesn't fail to parse them
|
||||
let safeText = content.replace(/\[([^\]]+)\]\(((\\\\|[A-Za-z]:\\)[^\)]+)\)/g, (match, title, url) => {
|
||||
let safeText = shieldCode(content).replace(/\[([^\]]+)\]\(((\\\\|[A-Za-z]:\\)[^\)]+)\)/g, (match, title, url) => {
|
||||
return `[${title}](${url.replace(/ /g, '%20')})`;
|
||||
});
|
||||
|
||||
@@ -1772,7 +1775,7 @@
|
||||
return `<a href="javascript:void(0);" class="copy-trigger" data-copy="${safeVal}" title="Click to copy">${safeDisplay}</a>`;
|
||||
});
|
||||
|
||||
preview.innerHTML = typeof marked !== 'undefined' ? marked.parse(safeText) : `<pre>${safeText}</pre>`;
|
||||
preview.innerHTML = typeof marked !== 'undefined' ? marked.parse(unshieldCode(safeText)) : `<pre>${unshieldCode(safeText)}</pre>`;
|
||||
formatCodeBlocks();
|
||||
}
|
||||
}
|
||||
@@ -1826,6 +1829,17 @@
|
||||
startDashboard();
|
||||
};
|
||||
|
||||
// Code spans and fenced blocks are set aside while the [[Badge:..]] / [[Copy:..]] / UNC
|
||||
// path rewrites run, so an article can show that syntax literally inside `code`
|
||||
const CODE_SHIELD = [];
|
||||
function shieldCode(text) {
|
||||
CODE_SHIELD.length = 0;
|
||||
return String(text).replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~|`[^`\n]+`)/g, m => `\u0000CODE${CODE_SHIELD.push(m) - 1}\u0000`);
|
||||
}
|
||||
function unshieldCode(text) {
|
||||
return String(text).replace(/\u0000CODE(\d+)\u0000/g, (m, i) => CODE_SHIELD[+i]);
|
||||
}
|
||||
|
||||
async function loadDocsList() {
|
||||
try {
|
||||
const res = await fetch('/get-docs-list?t=' + Date.now());
|
||||
@@ -1890,7 +1904,7 @@
|
||||
`;
|
||||
|
||||
// Encode spaces ONLY in UNC/Local paths so marked.js doesn't fail to parse them
|
||||
let safeText = text.replace(/\[([^\]]+)\]\(((\\\\|[A-Za-z]:\\)[^\)]+)\)/g, (match, title, url) => {
|
||||
let safeText = shieldCode(text).replace(/\[([^\]]+)\]\(((\\\\|[A-Za-z]:\\)[^\)]+)\)/g, (match, title, url) => {
|
||||
return `[${title}](${url.replace(/ /g, '%20')})`;
|
||||
});
|
||||
|
||||
@@ -1911,7 +1925,7 @@
|
||||
return `<a href="javascript:void(0);" class="copy-trigger" data-copy="${safeVal}" title="Click to copy">${safeDisplay}</a>`;
|
||||
});
|
||||
|
||||
const parsedContent = typeof marked !== 'undefined' ? marked.parse(safeText) : `<pre style="white-space: pre-wrap; font-family: inherit;">${safeText}</pre>`;
|
||||
const parsedContent = typeof marked !== 'undefined' ? marked.parse(unshieldCode(safeText)) : `<pre style="white-space: pre-wrap; font-family: inherit;">${unshieldCode(safeText)}</pre>`;
|
||||
document.getElementById('docContent').innerHTML = parsedContent;
|
||||
|
||||
// Keep the actual spaces in the editor so the user doesn't see ugly %20 encoding!
|
||||
@@ -1975,7 +1989,7 @@
|
||||
const rawText = editor.value;
|
||||
if (rawText) {
|
||||
// Encode spaces ONLY in UNC/Local paths so marked.js doesn't fail to parse them
|
||||
let safeText = rawText.replace(/\[([^\]]+)\]\(((\\\\|[A-Za-z]:\\)[^\)]+)\)/g, (match, title, url) => {
|
||||
let safeText = shieldCode(rawText).replace(/\[([^\]]+)\]\(((\\\\|[A-Za-z]:\\)[^\)]+)\)/g, (match, title, url) => {
|
||||
return `[${title}](${url.replace(/ /g, '%20')})`;
|
||||
});
|
||||
|
||||
@@ -1996,7 +2010,7 @@
|
||||
return `<a href="javascript:void(0);" class="copy-trigger" data-copy="${safeVal}" title="Click to copy">${safeDisplay}</a>`;
|
||||
});
|
||||
|
||||
preview.innerHTML = typeof marked !== 'undefined' ? marked.parse(safeText) : `<pre>${safeText}</pre>`;
|
||||
preview.innerHTML = typeof marked !== 'undefined' ? marked.parse(unshieldCode(safeText)) : `<pre>${unshieldCode(safeText)}</pre>`;
|
||||
formatCodeBlocks();
|
||||
}
|
||||
}
|
||||
@@ -2688,7 +2702,7 @@
|
||||
const nameText = s.Name || '--';
|
||||
const linkMatch = availableDocs.find(d => d.toLowerCase() === nameText.toLowerCase());
|
||||
const nameDisplay = linkMatch
|
||||
? `<a href="?doc=${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db;"><b>${nameText}</b></a>`
|
||||
? `<a href="/docs/${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db;"><b>${nameText}</b></a>`
|
||||
: `<b>${nameText}</b>`;
|
||||
return `
|
||||
<tr>
|
||||
@@ -2833,7 +2847,7 @@
|
||||
let hyperlinkedApps = sApps.map(app => {
|
||||
const linkMatch = availableDocs.find(d => d.toLowerCase() === app.toLowerCase());
|
||||
return linkMatch
|
||||
? `<a href="?doc=${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db; font-weight: bold;">${app}</a>`
|
||||
? `<a href="/docs/${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db; font-weight: bold;">${app}</a>`
|
||||
: app;
|
||||
});
|
||||
specialtySoftware = hyperlinkedApps.length > 0 ? hyperlinkedApps.join("<br>") : "None";
|
||||
@@ -3058,7 +3072,7 @@
|
||||
let hyperlinkedMissing = missing.map(app => {
|
||||
const linkMatch = availableDocs.find(d => d.toLowerCase() === app.toLowerCase());
|
||||
return linkMatch
|
||||
? `<a href="?doc=${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db; font-weight: bold;">${app}</a>`
|
||||
? `<a href="/docs/${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db; font-weight: bold;">${app}</a>`
|
||||
: app;
|
||||
});
|
||||
missingSoftwareDisplay = hyperlinkedMissing.length > 0 ? hyperlinkedMissing.join(", ") : "None";
|
||||
@@ -3171,7 +3185,7 @@
|
||||
let hyperlinkedMissing = missing.map(app => {
|
||||
const linkMatch = availableDocs.find(d => d.toLowerCase() === app.toLowerCase());
|
||||
return linkMatch
|
||||
? `<a href="?doc=${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db; font-weight: bold;">${app}</a>`
|
||||
? `<a href="/docs/${encodeURIComponent(linkMatch)}" target="_blank" onclick="event.stopPropagation();" style="color: #3498db; font-weight: bold;">${app}</a>`
|
||||
: app;
|
||||
});
|
||||
enriched["MissingSoftwareDisplay"] = hyperlinkedMissing.length > 0 ? hyperlinkedMissing.join(", ") : "None";
|
||||
|
||||
Reference in new issue
Block a user