Files
abogen/abogen/web/static/dashboard.js
T
JB b0875c7486 Enhance audiobook workflow UI with modal and styling updates
- Updated styles.css to introduce new modal and card styles for improved layout and responsiveness.
- Modified index.html to implement a modal for uploading files and settings, enhancing user experience.
- Refactored prepare_job.html to support a wizard-like interface for preparing jobs, including step indicators and dynamic content updates.
- Added functionality for gender selection and voice mixing in the speaker preparation section.
- Improved accessibility and usability with better hints and instructions throughout the forms.
2025-10-08 15:15:10 -07:00

211 lines
6.8 KiB
JavaScript

const initDashboard = () => {
const uploadModal = document.querySelector('[data-role="upload-modal"]');
const openModalButtons = document.querySelectorAll('[data-role="open-upload-modal"]');
const scope = uploadModal || document;
const profileSelect = scope.querySelector('[data-role="voice-profile"]');
const voiceField = scope.querySelector('[data-role="voice-field"]');
const voiceSelect = scope.querySelector('[data-role="voice-select"]');
const formulaField = scope.querySelector('[data-role="formula-field"]');
const formulaInput = scope.querySelector('[data-role="voice-formula"]');
const languageSelect = uploadModal?.querySelector("#language") || document.getElementById("language");
const sourceText = scope.querySelector('[data-role="source-text"]');
const previewEl = scope.querySelector('[data-role="text-preview"]');
const previewBody = scope.querySelector('[data-role="preview-body"]');
const charCountEl = scope.querySelector('[data-role="char-count"]');
const wordCountEl = scope.querySelector('[data-role="word-count"]');
let lastTrigger = null;
const openUploadModal = (trigger) => {
if (!uploadModal) return;
lastTrigger = trigger || null;
uploadModal.hidden = false;
uploadModal.dataset.open = "true";
document.body.classList.add("modal-open");
const focusTarget = uploadModal.querySelector("#source_file") || uploadModal.querySelector("#source_text") || uploadModal;
if (focusTarget instanceof HTMLElement) {
focusTarget.focus({ preventScroll: true });
}
};
const closeUploadModal = () => {
if (!uploadModal || uploadModal.hidden) {
return;
}
uploadModal.hidden = true;
delete uploadModal.dataset.open;
document.body.classList.remove("modal-open");
if (lastTrigger && lastTrigger instanceof HTMLElement) {
lastTrigger.focus({ preventScroll: true });
}
};
openModalButtons.forEach((button) => {
button.addEventListener("click", (event) => {
event.preventDefault();
openUploadModal(button);
});
});
if (uploadModal) {
uploadModal.addEventListener("click", (event) => {
const target = event.target;
if (target instanceof Element && target.closest('[data-role="upload-modal-close"]')) {
event.preventDefault();
closeUploadModal();
}
});
}
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && uploadModal && !uploadModal.hidden) {
closeUploadModal();
}
});
const hydrateDefaultVoice = () => {
if (!voiceSelect) return;
const defaultVoice = voiceSelect.dataset.default;
if (!defaultVoice) return;
const option = voiceSelect.querySelector(`option[value="${defaultVoice}"]`);
if (option) {
voiceSelect.value = defaultVoice;
}
};
const selectFirstProfileIfAvailable = () => {
if (!profileSelect) return false;
const saved = Array.from(profileSelect.options).filter(
(option) => option.value && option.value !== "__standard" && option.value !== "__formula",
);
if (!saved.length) {
profileSelect.value = "__standard";
return false;
}
saved[0].selected = true;
return true;
};
const applySavedProfile = (option) => {
if (!option) return;
const presetFormula = option.dataset.formula || "";
const profileLang = option.dataset.language || "";
if (formulaInput) {
formulaInput.value = presetFormula;
formulaInput.readOnly = true;
formulaInput.dataset.state = "locked";
}
if (profileLang && languageSelect) {
languageSelect.value = profileLang;
}
};
const updateVoiceControls = () => {
if (!profileSelect) {
return;
}
const value = profileSelect.value || "__standard";
const isStandard = value === "__standard";
const isFormula = value === "__formula";
const isSavedProfile = !isStandard && !isFormula;
const showVoiceField = isStandard;
if (voiceField) {
voiceField.hidden = !showVoiceField;
voiceField.setAttribute("aria-hidden", showVoiceField ? "false" : "true");
voiceField.dataset.state = showVoiceField ? "visible" : "hidden";
}
if (voiceSelect) {
voiceSelect.disabled = !isStandard;
voiceSelect.dataset.state = isStandard ? "editable" : "locked";
if (isStandard) {
hydrateDefaultVoice();
}
}
if (isSavedProfile) {
applySavedProfile(profileSelect.selectedOptions[0] || null);
} else if (!isFormula && formulaInput) {
formulaInput.value = "";
}
const showFormulaField = isFormula;
if (formulaField) {
const shouldShow = showFormulaField;
formulaField.hidden = !shouldShow;
formulaField.setAttribute("aria-hidden", shouldShow ? "false" : "true");
formulaField.dataset.state = shouldShow ? "visible" : "hidden";
}
if (formulaInput) {
if (isFormula) {
formulaInput.disabled = false;
formulaInput.readOnly = false;
formulaInput.dataset.state = "editable";
} else if (isSavedProfile) {
formulaInput.disabled = false;
formulaInput.readOnly = true;
formulaInput.dataset.state = "locked";
} else {
formulaInput.disabled = true;
formulaInput.readOnly = true;
formulaInput.value = "";
formulaInput.dataset.state = "editable";
}
}
};
const updatePreview = () => {
if (!sourceText || !previewBody || !charCountEl || !wordCountEl) {
return;
}
const raw = sourceText.value || "";
const trimmed = raw.trim();
const charCount = raw.length;
const wordCount = trimmed ? trimmed.split(/\s+/).length : 0;
const charLabel = `${charCount.toLocaleString()} ${charCount === 1 ? "character" : "characters"}`;
const wordLabel = `${wordCount.toLocaleString()} ${wordCount === 1 ? "word" : "words"}`;
charCountEl.textContent = charLabel;
wordCountEl.textContent = wordLabel;
if (!trimmed) {
previewBody.textContent = "Paste text to see a live preview and character count.";
if (previewEl) {
previewEl.setAttribute("data-state", "empty");
}
return;
}
const snippet = trimmed.length > 1200 ? `${trimmed.slice(0, 1200)}…` : trimmed;
previewBody.textContent = snippet;
if (previewEl) {
previewEl.setAttribute("data-state", "ready");
}
};
if (profileSelect) {
const hasSaved = selectFirstProfileIfAvailable();
profileSelect.addEventListener("change", updateVoiceControls);
updateVoiceControls();
if (!hasSaved) {
hydrateDefaultVoice();
}
} else {
hydrateDefaultVoice();
}
if (sourceText) {
sourceText.addEventListener("input", updatePreview);
updatePreview();
}
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initDashboard, { once: true });
} else {
initDashboard();
}