Add conflict resolution diff UI and app branding

This commit is contained in:
2026-05-13 16:25:28 -04:00
parent 780ff3a02c
commit d359baa010
83 changed files with 4523 additions and 3735 deletions

View File

@@ -1,8 +1,10 @@
(function () {
const DB_NAME = "md-hub-cache";
const DB_VERSION = 1;
const DB_VERSION = 3;
const STORE_DOCUMENTS = "documents";
const STORE_CONTENT = "content";
const STORE_PAGES = "pages";
const STORE_PENDING_EDITS = "pending_edits";
function openDB() {
return new Promise(function (resolve, reject) {
@@ -21,10 +23,22 @@
if (!db.objectStoreNames.contains(STORE_CONTENT)) {
db.createObjectStore(STORE_CONTENT, { keyPath: "path" });
}
if (!db.objectStoreNames.contains(STORE_PAGES)) {
db.createObjectStore(STORE_PAGES, { keyPath: "path" });
}
if (!db.objectStoreNames.contains(STORE_PENDING_EDITS)) {
db.createObjectStore(STORE_PENDING_EDITS, { keyPath: "path" });
}
};
});
}
function normalizePath(path) {
if (!path) return "/";
if (path === "/") return path;
return path.replace(/\/$/, "") || "/";
}
function cacheDocuments(documents) {
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
@@ -91,10 +105,158 @@
});
}
function cachePage(path, html, title) {
const normalizedPath = normalizePath(path);
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PAGES, "readwrite");
const store = tx.objectStore(STORE_PAGES);
store.put({
path: normalizedPath,
html: html,
title: title,
cachedAt: Date.now(),
});
tx.oncomplete = function () {
resolve();
};
tx.onerror = function () {
reject(tx.error);
};
});
});
}
function getCachedPage(path) {
const normalizedPath = normalizePath(path);
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PAGES, "readonly");
const store = tx.objectStore(STORE_PAGES);
const request = store.get(normalizedPath);
request.onsuccess = function () {
resolve(request.result);
};
request.onerror = function () {
reject(request.error);
};
});
});
}
function putPendingEdit(path, content, baseHash) {
const normalizedPath = normalizePath(path);
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PENDING_EDITS, "readwrite");
const store = tx.objectStore(STORE_PENDING_EDITS);
store.put({
path: normalizedPath,
content: content,
baseHash: baseHash || "",
conflict: null,
retries: 0,
updatedAt: Date.now(),
});
tx.oncomplete = function () {
resolve();
};
tx.onerror = function () {
reject(tx.error);
};
});
});
}
function markPendingEditConflict(path, conflict) {
const normalizedPath = normalizePath(path);
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PENDING_EDITS, "readwrite");
const store = tx.objectStore(STORE_PENDING_EDITS);
const request = store.get(normalizedPath);
request.onsuccess = function () {
const record = request.result;
if (!record) {
resolve();
return;
}
record.conflict = conflict;
record.updatedAt = Date.now();
store.put(record);
};
tx.oncomplete = function () {
resolve();
};
tx.onerror = function () {
reject(tx.error);
};
});
});
}
function getPendingEdits() {
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PENDING_EDITS, "readonly");
const store = tx.objectStore(STORE_PENDING_EDITS);
const request = store.getAll();
request.onsuccess = function () {
resolve(request.result || []);
};
request.onerror = function () {
reject(request.error);
};
});
});
}
function getPendingEdit(path) {
const normalizedPath = normalizePath(path);
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PENDING_EDITS, "readonly");
const store = tx.objectStore(STORE_PENDING_EDITS);
const request = store.get(normalizedPath);
request.onsuccess = function () {
resolve(request.result);
};
request.onerror = function () {
reject(request.error);
};
});
});
}
function deletePendingEdit(path) {
const normalizedPath = normalizePath(path);
return openDB().then(function (db) {
return new Promise(function (resolve, reject) {
const tx = db.transaction(STORE_PENDING_EDITS, "readwrite");
const store = tx.objectStore(STORE_PENDING_EDITS);
store.delete(normalizedPath);
tx.oncomplete = function () {
resolve();
};
tx.onerror = function () {
reject(tx.error);
};
});
});
}
window.MDHubCache = {
cacheDocuments: cacheDocuments,
cacheContent: cacheContent,
cachePage: cachePage,
putPendingEdit: putPendingEdit,
markPendingEditConflict: markPendingEditConflict,
getPendingEdits: getPendingEdits,
getPendingEdit: getPendingEdit,
deletePendingEdit: deletePendingEdit,
getCachedDocuments: getCachedDocuments,
getCachedContent: getCachedContent,
getCachedPage: getCachedPage,
normalizePath: normalizePath,
};
})();