- Dynamic miller column sizing with flexbox layout - Root column (160px), middle slices (48px), active column (flex) - Auto-scroll browser to rightmost column on navigation - Offline indicator UI and IndexedDB cache integration - /api/documents endpoint for client-side document caching - Breadcrumb navigation in document content area - FTS5 search with SQLite virtual table - Client-side Mermaid and KaTeX rendering via CDN - Deep sample content (advanced-topics/raft-deep-dive) - Border removal (only search button keeps radius) - Full viewport layout with proper borders between sidebar/content
101 lines
2.9 KiB
JavaScript
101 lines
2.9 KiB
JavaScript
(function () {
|
|
const DB_NAME = "md-hub-cache";
|
|
const DB_VERSION = 1;
|
|
const STORE_DOCUMENTS = "documents";
|
|
const STORE_CONTENT = "content";
|
|
|
|
function openDB() {
|
|
return new Promise(function (resolve, reject) {
|
|
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
request.onerror = function () {
|
|
reject(request.error);
|
|
};
|
|
request.onsuccess = function () {
|
|
resolve(request.result);
|
|
};
|
|
request.onupgradeneeded = function (event) {
|
|
const db = event.target.result;
|
|
if (!db.objectStoreNames.contains(STORE_DOCUMENTS)) {
|
|
db.createObjectStore(STORE_DOCUMENTS, { keyPath: "path" });
|
|
}
|
|
if (!db.objectStoreNames.contains(STORE_CONTENT)) {
|
|
db.createObjectStore(STORE_CONTENT, { keyPath: "path" });
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
function cacheDocuments(documents) {
|
|
return openDB().then(function (db) {
|
|
return new Promise(function (resolve, reject) {
|
|
const tx = db.transaction(STORE_DOCUMENTS, "readwrite");
|
|
const store = tx.objectStore(STORE_DOCUMENTS);
|
|
documents.forEach(function (doc) {
|
|
store.put(doc);
|
|
});
|
|
tx.oncomplete = function () {
|
|
resolve();
|
|
};
|
|
tx.onerror = function () {
|
|
reject(tx.error);
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
function cacheContent(path, html, title) {
|
|
return openDB().then(function (db) {
|
|
return new Promise(function (resolve, reject) {
|
|
const tx = db.transaction(STORE_CONTENT, "readwrite");
|
|
const store = tx.objectStore(STORE_CONTENT);
|
|
store.put({ path: path, html: html, title: title, cachedAt: Date.now() });
|
|
tx.oncomplete = function () {
|
|
resolve();
|
|
};
|
|
tx.onerror = function () {
|
|
reject(tx.error);
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
function getCachedDocuments() {
|
|
return openDB().then(function (db) {
|
|
return new Promise(function (resolve, reject) {
|
|
const tx = db.transaction(STORE_DOCUMENTS, "readonly");
|
|
const store = tx.objectStore(STORE_DOCUMENTS);
|
|
const request = store.getAll();
|
|
request.onsuccess = function () {
|
|
resolve(request.result);
|
|
};
|
|
request.onerror = function () {
|
|
reject(request.error);
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
function getCachedContent(path) {
|
|
return openDB().then(function (db) {
|
|
return new Promise(function (resolve, reject) {
|
|
const tx = db.transaction(STORE_CONTENT, "readonly");
|
|
const store = tx.objectStore(STORE_CONTENT);
|
|
const request = store.get(path);
|
|
request.onsuccess = function () {
|
|
resolve(request.result);
|
|
};
|
|
request.onerror = function () {
|
|
reject(request.error);
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
window.MDHubCache = {
|
|
cacheDocuments: cacheDocuments,
|
|
cacheContent: cacheContent,
|
|
getCachedDocuments: getCachedDocuments,
|
|
getCachedContent: getCachedContent,
|
|
};
|
|
})();
|