Smooth persisted app shell loading
This commit is contained in:
parent
7ae37d0bb7
commit
2fc8ad8ce7
145
static/app.js
145
static/app.js
|
|
@ -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");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -419,3 +419,13 @@ label.tool-action-button {
|
|||
padding: 5px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
body.app-loading .container {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
transition: opacity 120ms ease-out;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue