Smooth persisted app shell loading

This commit is contained in:
Sean McElwain 2026-06-10 00:09:53 -05:00
parent 7ae37d0bb7
commit 2fc8ad8ce7
3 changed files with 153 additions and 4 deletions

View File

@ -256,12 +256,30 @@ async function loadDocumentTypes() {
defaultDocumentTypes = json.document_types || [];
renderDefaultDocumentOptions();
await loadUploadedJsonOptions();
const savedSelectionRaw = localStorage.getItem("utilityAppSelectedDocument");
let savedSelection = null;
try {
savedSelection = savedSelectionRaw ? JSON.parse(savedSelectionRaw) : null;
} catch {
savedSelection = null;
}
if (savedSelection?.kind === "uploaded" && savedSelection.id) {
await loadUploadedJson(savedSelection.id);
return;
}
if (savedSelection?.kind === "default" && savedSelection.id) {
await loadDefaultDocumentType(savedSelection.id);
return;
}
if (defaultDocumentTypes.length > 0) {
await loadDefaultDocumentType(defaultDocumentTypes[0].id);
}
await loadUploadedJsonOptions();
}
function renderDefaultDocumentOptions() {
@ -292,6 +310,14 @@ async function loadDefaultDocumentType(documentTypeId) {
renderDocumentType(currentDocumentType);
setActivePicker("default", documentTypeId, currentDocumentType.name || documentTypeId);
closeDocumentPicker();
localStorage.setItem("utilityAppSelectedDocument", JSON.stringify({
kind: "default",
id: documentTypeId,
label: currentDocumentType.name || documentTypeId
}));
restoreSavedFormData();
}
async function loadUploadedJsonOptions() {
@ -375,6 +401,14 @@ async function loadUploadedJson(filename) {
loadPresetObject(result.json);
setActivePicker("uploaded", filename, filename);
closeDocumentPicker();
localStorage.setItem("utilityAppSelectedDocument", JSON.stringify({
kind: "uploaded",
id: filename,
label: filename
}));
restoreSavedFormData();
setStatus(`Loaded uploaded JSON: ${filename}`);
}
@ -484,7 +518,9 @@ clearFormButton.addEventListener("click", () => {
const el = document.getElementById(field.name);
if (el) el.value = "";
}
setStatus("");
localStorage.removeItem(getFormStorageKey());
setStatus("Cleared form data.");
});
presetFileInput.addEventListener("change", async event => {
@ -611,3 +647,106 @@ document.addEventListener("click", event => {
closeDocumentPicker();
}
});
function showView(viewId) {
document.querySelectorAll(".app-view").forEach(view => {
view.classList.toggle("active", view.id === viewId);
});
document.querySelectorAll(".nav-button").forEach(button => {
button.classList.toggle("active", button.dataset.view === viewId);
});
localStorage.setItem("utilityAppActiveView", viewId);
}
document.querySelectorAll(".nav-button").forEach(button => {
button.addEventListener("click", () => {
showView(button.dataset.view);
});
});
const mainPageContent = document.getElementById("mainPageContent");
const saveMainPageButton = document.getElementById("saveMainPageButton");
const resetMainPageButton = document.getElementById("resetMainPageButton");
function loadMainPageContent() {
const saved = localStorage.getItem("utilityAppMainPageContent");
if (saved) {
mainPageContent.innerHTML = saved;
}
}
saveMainPageButton.addEventListener("click", () => {
localStorage.setItem("utilityAppMainPageContent", mainPageContent.innerHTML);
setStatus("Saved main page text in this browser.");
});
resetMainPageButton.addEventListener("click", () => {
localStorage.removeItem("utilityAppMainPageContent");
mainPageContent.innerHTML = `
<p>Select a tool above. This main page text is editable in the browser for quick notes, instructions, or workflow reminders.</p>
<p><strong>Current tools:</strong> Document Generator and Document Processor.</p>
`;
setStatus("Reset main page text.");
});
loadMainPageContent();
function getFormStorageKey() {
const selectedRaw = localStorage.getItem("utilityAppSelectedDocument");
let selected = null;
try {
selected = selectedRaw ? JSON.parse(selectedRaw) : null;
} catch {
selected = null;
}
const kind = selected?.kind || activePickerKind || "default";
const id = selected?.id || activePickerId || currentDocumentType?.id || "unknown";
return `utilityAppFormData:${kind}:${id}`;
}
function saveCurrentFormData() {
if (!currentFields || currentFields.length === 0) return;
const data = getFormData(false);
localStorage.setItem(getFormStorageKey(), JSON.stringify(data));
}
function restoreSavedFormData() {
const saved = localStorage.getItem(getFormStorageKey());
if (!saved) return;
try {
const data = JSON.parse(saved);
applyDataToForm(data);
} catch {
return;
}
}
fieldsContainer.addEventListener("input", () => {
saveCurrentFormData();
});
fieldsContainer.addEventListener("change", () => {
saveCurrentFormData();
});
window.addEventListener("beforeunload", () => {
saveCurrentFormData();
});
const savedActiveView = localStorage.getItem("utilityAppActiveView");
if (savedActiveView) {
showView(savedActiveView);
}
requestAnimationFrame(() => {
document.body.classList.remove("app-loading");
});

View File

@ -4,7 +4,7 @@
<title>Utility App</title>
<link rel="stylesheet" href="/static/styles.css?v=shell1">
</head>
<body>
<body class="app-loading">
<header class="app-header">
<div class="app-header-inner">
<div class="app-title">Utility App</div>

View File

@ -419,3 +419,13 @@ label.tool-action-button {
padding: 5px 6px;
}
}
body.app-loading .container {
opacity: 0;
}
.container {
transition: opacity 120ms ease-out;
}