Revamp overall theme
This commit is contained in:
+243
-12
@@ -1,7 +1,6 @@
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const settingsNotice = $("settingsNotice");
|
||||
const settingsThemeSelect = $("settingsThemeSelect");
|
||||
const settingsThemeName = $("settingsThemeName");
|
||||
const currentUsernameInput = $("currentUsername");
|
||||
const newUsernameInput = $("newUsername");
|
||||
const usernamePasswordInput = $("usernamePassword");
|
||||
@@ -12,6 +11,24 @@ const usernameForm = $("usernameForm");
|
||||
const passwordForm = $("passwordForm");
|
||||
const settingsNavItems = document.querySelectorAll("[data-settings-view]");
|
||||
const settingsPanels = document.querySelectorAll("[data-settings-panel]");
|
||||
const createAiCharacterBtn = $("createAiCharacter");
|
||||
const createUserPersonaBtn = $("createUserPersona");
|
||||
const settingsAiCharacterList = $("settingsAiCharacterList");
|
||||
const settingsUserPersonaList = $("settingsUserPersonaList");
|
||||
const settingsPersonaModal = $("settingsPersonaModal");
|
||||
const settingsPersonaClose = $("settingsPersonaClose");
|
||||
const settingsPersonaForm = $("settingsPersonaForm");
|
||||
const settingsPersonaFormTitle = $("settingsPersonaFormTitle");
|
||||
const settingsPersonaFormNotice = $("settingsPersonaFormNotice");
|
||||
const settingsPersonaHelper = $("settingsPersonaHelper");
|
||||
const settingsPersonaType = $("settingsPersonaType");
|
||||
const settingsPersonaName = $("settingsPersonaName");
|
||||
const settingsPersonaPronouns = $("settingsPersonaPronouns");
|
||||
const settingsPersonaAppearance = $("settingsPersonaAppearance");
|
||||
const settingsPersonaBackground = $("settingsPersonaBackground");
|
||||
const settingsPersonaDetails = $("settingsPersonaDetails");
|
||||
const settingsPersonaExamplesField = $("settingsPersonaExamplesField");
|
||||
const settingsPersonaExampleDialogues = $("settingsPersonaExampleDialogues");
|
||||
|
||||
const themes = {
|
||||
"fakegpt": {name: "FakeGPT"},
|
||||
@@ -22,6 +39,11 @@ const themes = {
|
||||
"confusity": {name: "Confusity"}
|
||||
};
|
||||
|
||||
let assistantPersonas = [];
|
||||
let userPersonas = [];
|
||||
let publishedPersonaIds = new Set();
|
||||
let editingPersonaId = null;
|
||||
|
||||
async function request(url, data, method = "POST") {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
@@ -35,34 +57,220 @@ async function request(url, data, method = "POST") {
|
||||
}
|
||||
}
|
||||
|
||||
const get = (url) => request(url, null, "GET");
|
||||
const post = (url, data) => request(url, data, "POST");
|
||||
const put = (url, data) => request(url, data, "PUT");
|
||||
const del = (url) => request(url, null, "DELETE");
|
||||
|
||||
function setNotice(message, state = "") {
|
||||
settingsNotice.textContent = message;
|
||||
settingsNotice.className = state ? `status ${state}` : "status";
|
||||
}
|
||||
|
||||
function setPersonaNotice(message = "", state = "") {
|
||||
if (!message) {
|
||||
settingsPersonaFormNotice.textContent = "";
|
||||
settingsPersonaFormNotice.className = "status hidden";
|
||||
return;
|
||||
}
|
||||
settingsPersonaFormNotice.textContent = message;
|
||||
settingsPersonaFormNotice.className = state ? `status ${state}` : "status";
|
||||
}
|
||||
|
||||
function applyTheme(themeKey, persist = true) {
|
||||
const nextTheme = themes[themeKey] ? themeKey : "fakegpt";
|
||||
document.body.dataset.theme = nextTheme;
|
||||
document.title = "Settings";
|
||||
settingsThemeSelect.value = nextTheme;
|
||||
if (settingsThemeName) settingsThemeName.textContent = themes[nextTheme].name;
|
||||
if (persist) localStorage.setItem("krishd-theme", nextTheme);
|
||||
}
|
||||
|
||||
function setSettingsView(view) {
|
||||
settingsNavItems.forEach((item) => item.classList.toggle("active", item.dataset.settingsView === view));
|
||||
settingsPanels.forEach((panel) => panel.classList.toggle("active", panel.dataset.settingsPanel === view));
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set("view", view);
|
||||
history.replaceState(null, "", `${window.location.pathname}?${params.toString()}`);
|
||||
}
|
||||
|
||||
function formatMeta(persona) {
|
||||
return persona.pronouns ? `Pronouns: ${persona.pronouns}` : "Pronouns: n/a";
|
||||
}
|
||||
|
||||
function syncPersonaExamplesField() {
|
||||
const isAssistant = settingsPersonaType.value === "assistant";
|
||||
settingsPersonaExamplesField.classList.toggle("hidden", !isAssistant);
|
||||
settingsPersonaHelper.textContent = isAssistant
|
||||
? "AI Characters define who the character is, how they speak, and what kind of scene they lead."
|
||||
: "User personas define who you are when starting roleplay chats with AI Characters.";
|
||||
$("settingsPersonaSave").textContent = isAssistant ? "Save AI Character" : "Save persona";
|
||||
}
|
||||
|
||||
function openPersonaModal(persona = null, personaType = "assistant") {
|
||||
editingPersonaId = persona ? persona.id : null;
|
||||
settingsPersonaType.disabled = Boolean(persona);
|
||||
settingsPersonaType.value = persona ? persona.persona_type : personaType;
|
||||
settingsPersonaFormTitle.textContent = persona
|
||||
? `Edit ${settingsPersonaType.value === "assistant" ? "AI Character" : "persona"}`
|
||||
: `Create ${personaType === "assistant" ? "AI Character" : "persona"}`;
|
||||
settingsPersonaName.value = persona?.name || "";
|
||||
settingsPersonaPronouns.value = persona?.pronouns || "";
|
||||
settingsPersonaAppearance.value = persona?.appearance || "";
|
||||
settingsPersonaBackground.value = persona?.background || "";
|
||||
settingsPersonaDetails.value = persona?.details || "";
|
||||
settingsPersonaExampleDialogues.value = persona?.example_dialogues || "";
|
||||
syncPersonaExamplesField();
|
||||
setPersonaNotice("");
|
||||
settingsPersonaModal.classList.remove("hidden");
|
||||
settingsPersonaName.focus();
|
||||
}
|
||||
|
||||
function closePersonaModal() {
|
||||
settingsPersonaModal.classList.add("hidden");
|
||||
editingPersonaId = null;
|
||||
setPersonaNotice("");
|
||||
}
|
||||
|
||||
function renderPersonaList(personas, listElement, type) {
|
||||
listElement.innerHTML = "";
|
||||
if (!personas.length) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "status persona-status";
|
||||
empty.textContent = type === "assistant"
|
||||
? "No AI Characters yet. Create one to get started."
|
||||
: "No user personas yet. Create one to talk as someone specific.";
|
||||
listElement.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
personas.forEach((persona) => {
|
||||
const item = document.createElement("div");
|
||||
const titleRow = document.createElement("div");
|
||||
const title = document.createElement("h4");
|
||||
const tag = document.createElement("span");
|
||||
const meta = document.createElement("p");
|
||||
const actions = document.createElement("div");
|
||||
|
||||
item.className = "persona-item";
|
||||
titleRow.className = "persona-title-row";
|
||||
actions.className = "persona-item-actions";
|
||||
tag.className = `persona-role-tag ${type === "assistant" ? "ai" : "user"}`;
|
||||
tag.textContent = type === "assistant" ? "AI Character" : "User";
|
||||
title.textContent = persona.name;
|
||||
meta.textContent = formatMeta(persona);
|
||||
|
||||
[
|
||||
["Edit", () => openPersonaModal(persona)],
|
||||
[type === "assistant" && publishedPersonaIds.has(persona.id) ? "Update listing" : type === "assistant" ? "Publish" : null, () => publishPersona(persona.id)],
|
||||
["Delete", () => deletePersona(persona.id)]
|
||||
]
|
||||
.filter(([label]) => Boolean(label))
|
||||
.forEach(([label, handler]) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.textContent = label;
|
||||
button.addEventListener("click", handler);
|
||||
actions.appendChild(button);
|
||||
});
|
||||
|
||||
if (type === "assistant" && publishedPersonaIds.has(persona.id)) {
|
||||
const unpublishButton = document.createElement("button");
|
||||
unpublishButton.type = "button";
|
||||
unpublishButton.textContent = "Unpublish";
|
||||
unpublishButton.addEventListener("click", () => unpublishPersona(persona.id));
|
||||
actions.appendChild(unpublishButton);
|
||||
}
|
||||
|
||||
titleRow.append(title, tag);
|
||||
item.append(titleRow, meta, actions);
|
||||
listElement.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadProfile() {
|
||||
const res = await request("/settings/profile", null, "GET");
|
||||
const res = await get("/settings/profile");
|
||||
if (res.error) {
|
||||
setNotice(res.error, "error");
|
||||
return false;
|
||||
}
|
||||
currentUsernameInput.value = res.username || "";
|
||||
newUsernameInput.value = res.username || "";
|
||||
return true;
|
||||
}
|
||||
|
||||
async function loadPersonas() {
|
||||
const res = await get("/personas");
|
||||
if (res.error) {
|
||||
setNotice(res.error, "error");
|
||||
return false;
|
||||
}
|
||||
assistantPersonas = res.assistantPersonas || [];
|
||||
userPersonas = res.userPersonas || [];
|
||||
publishedPersonaIds = new Set(res.publishedPersonaIds || []);
|
||||
renderPersonaList(assistantPersonas, settingsAiCharacterList, "assistant");
|
||||
renderPersonaList(userPersonas, settingsUserPersonaList, "user");
|
||||
return true;
|
||||
}
|
||||
|
||||
async function savePersona() {
|
||||
const payload = {
|
||||
personaType: settingsPersonaType.value,
|
||||
name: settingsPersonaName.value.trim(),
|
||||
pronouns: settingsPersonaPronouns.value.trim(),
|
||||
appearance: settingsPersonaAppearance.value.trim(),
|
||||
background: settingsPersonaBackground.value.trim(),
|
||||
details: settingsPersonaDetails.value.trim(),
|
||||
exampleDialogues: settingsPersonaType.value === "assistant" ? settingsPersonaExampleDialogues.value.trim() : ""
|
||||
};
|
||||
|
||||
if (!payload.name) {
|
||||
setPersonaNotice("Name is required.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const res = editingPersonaId
|
||||
? await put(`/personas/${editingPersonaId}`, payload)
|
||||
: await post("/personas", payload);
|
||||
|
||||
if (res.error) {
|
||||
setPersonaNotice(res.error, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
await loadPersonas();
|
||||
closePersonaModal();
|
||||
setSettingsView(payload.personaType === "assistant" ? "my-ai-characters" : "my-personas");
|
||||
setNotice(editingPersonaId ? "Persona updated." : "Persona created.", "success");
|
||||
}
|
||||
|
||||
async function deletePersona(id) {
|
||||
const res = await del(`/personas/${id}`);
|
||||
if (res.error) {
|
||||
setNotice(res.error, "error");
|
||||
return;
|
||||
}
|
||||
currentUsernameInput.value = res.username || "";
|
||||
newUsernameInput.value = res.username || "";
|
||||
setNotice("Settings loaded.");
|
||||
await loadPersonas();
|
||||
setNotice("Persona deleted.", "success");
|
||||
}
|
||||
|
||||
async function publishPersona(id) {
|
||||
const res = await post(`/personas/${id}/publish`, {});
|
||||
if (res.error) {
|
||||
setNotice(res.error, "error");
|
||||
return;
|
||||
}
|
||||
await loadPersonas();
|
||||
setNotice("AI Character published.", "success");
|
||||
}
|
||||
|
||||
async function unpublishPersona(id) {
|
||||
const res = await post(`/personas/${id}/unpublish`, {});
|
||||
if (res.error) {
|
||||
setNotice(res.error, "error");
|
||||
return;
|
||||
}
|
||||
await loadPersonas();
|
||||
setNotice("AI Character unpublished.", "success");
|
||||
}
|
||||
|
||||
usernameForm.addEventListener("submit", async (event) => {
|
||||
@@ -87,17 +295,17 @@ usernameForm.addEventListener("submit", async (event) => {
|
||||
passwordForm.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
const currentPassword = currentPasswordInput.value;
|
||||
const newPassword = newPasswordInput.value;
|
||||
const nextPassword = newPasswordInput.value;
|
||||
const repeatPassword = repeatPasswordInput.value;
|
||||
if (!currentPassword || !newPassword || !repeatPassword) {
|
||||
if (!currentPassword || !nextPassword || !repeatPassword) {
|
||||
setNotice("Fill in all password fields.", "error");
|
||||
return;
|
||||
}
|
||||
if (newPassword !== repeatPassword) {
|
||||
if (nextPassword !== repeatPassword) {
|
||||
setNotice("New passwords do not match.", "error");
|
||||
return;
|
||||
}
|
||||
const res = await request("/settings/password", {currentPassword, newPassword}, "PUT");
|
||||
const res = await request("/settings/password", {currentPassword, newPassword: nextPassword}, "PUT");
|
||||
if (res.error) {
|
||||
setNotice(res.error, "error");
|
||||
return;
|
||||
@@ -114,9 +322,32 @@ settingsThemeSelect.addEventListener("change", (event) => {
|
||||
});
|
||||
|
||||
settingsNavItems.forEach((item) => item.addEventListener("click", () => setSettingsView(item.dataset.settingsView)));
|
||||
createAiCharacterBtn.addEventListener("click", () => openPersonaModal(null, "assistant"));
|
||||
createUserPersonaBtn.addEventListener("click", () => openPersonaModal(null, "user"));
|
||||
settingsPersonaClose.addEventListener("click", closePersonaModal);
|
||||
settingsPersonaModal.addEventListener("click", (event) => {
|
||||
if (event.target === settingsPersonaModal) closePersonaModal();
|
||||
});
|
||||
settingsPersonaType.addEventListener("change", syncPersonaExamplesField);
|
||||
settingsPersonaForm.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
void savePersona();
|
||||
});
|
||||
|
||||
window.addEventListener("load", async () => {
|
||||
applyTheme(localStorage.getItem("krishd-theme") || "fakegpt", false);
|
||||
setSettingsView("personal");
|
||||
await loadProfile();
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const view = params.get("view") || "personal";
|
||||
setSettingsView(view);
|
||||
const [profileOk, personasOk] = await Promise.all([loadProfile(), loadPersonas()]);
|
||||
if (profileOk && personasOk) setNotice("Settings loaded.");
|
||||
|
||||
const create = params.get("create");
|
||||
if (create === "assistant") {
|
||||
setSettingsView("my-ai-characters");
|
||||
openPersonaModal(null, "assistant");
|
||||
} else if (create === "user") {
|
||||
setSettingsView("my-personas");
|
||||
openPersonaModal(null, "user");
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user