implement various changes to styling
This commit is contained in:
+38
-36
@@ -163,12 +163,6 @@
|
|||||||
|
|
||||||
<div id="messages" class="messages-panel messages-panel-focus"></div>
|
<div id="messages" class="messages-panel messages-panel-focus"></div>
|
||||||
|
|
||||||
<div id="emptyState" class="empty-state">
|
|
||||||
<p class="eyebrow">No messages yet</p>
|
|
||||||
<h3>Write something when you're ready.</h3>
|
|
||||||
<p>This can stay a plain chat. Personas and shared presets are optional.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-shell input-shell-focus">
|
<div class="input-shell input-shell-focus">
|
||||||
<div class="input-meta">
|
<div class="input-meta">
|
||||||
<p class="composer-hint">Enter sends. Shift+Enter adds a new line.</p>
|
<p class="composer-hint">Enter sends. Shift+Enter adds a new line.</p>
|
||||||
@@ -184,40 +178,48 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="personaModal" class="modal-overlay hidden">
|
<div id="personaModal" class="modal-overlay hidden">
|
||||||
<div class="modal-card">
|
<div class="modal-card persona-modal-card">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h3 id="personaFormTitle">Create persona</h3>
|
<div class="persona-modal-heading">
|
||||||
|
<p class="eyebrow">Persona editor</p>
|
||||||
|
<h3 id="personaFormTitle">Create persona</h3>
|
||||||
|
</div>
|
||||||
<button id="personaClose" type="button" class="icon-button">Cancel</button>
|
<button id="personaClose" type="button" class="icon-button">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
<form id="personaForm" class="persona-form">
|
<form id="personaForm" class="persona-form">
|
||||||
<label class="field">
|
<div class="persona-form-intro">
|
||||||
<span>Persona for</span>
|
<p class="persona-helper">AI personas shape the assistant. User personas add context for how replies should read you.</p>
|
||||||
<select id="personaType">
|
</div>
|
||||||
<option value="assistant">AI persona</option>
|
<p id="personaFormNotice" class="status hidden"></p>
|
||||||
<option value="user">Your persona</option>
|
<div class="persona-form-grid">
|
||||||
</select>
|
<label class="field field-wide">
|
||||||
</label>
|
<span>Persona for</span>
|
||||||
<p class="persona-helper">AI personas shape the assistant. Your persona gives the model user-side context.</p>
|
<select id="personaType">
|
||||||
<label class="field">
|
<option value="assistant">AI persona</option>
|
||||||
<span>Name</span>
|
<option value="user">Your persona</option>
|
||||||
<input id="personaName" placeholder="Persona name" required/>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<span>Pronouns</span>
|
<span>Persona name</span>
|
||||||
<input id="personaPronouns" placeholder="e.g. she/her, they/them"/>
|
<input id="personaName" placeholder="Persona name" required/>
|
||||||
</label>
|
</label>
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<span>Appearance</span>
|
<span>Persona pronouns</span>
|
||||||
<textarea id="personaAppearance" rows="2" placeholder="Describe their look"></textarea>
|
<input id="personaPronouns" placeholder="e.g. she/her, they/them"/>
|
||||||
</label>
|
</label>
|
||||||
<label class="field">
|
<label class="field field-wide">
|
||||||
<span>Background</span>
|
<span>Appearance</span>
|
||||||
<textarea id="personaBackground" rows="2" placeholder="Backstory or origins"></textarea>
|
<textarea id="personaAppearance" rows="3" placeholder="Describe their look"></textarea>
|
||||||
</label>
|
</label>
|
||||||
<label class="field">
|
<label class="field field-wide">
|
||||||
<span>Details</span>
|
<span>Background</span>
|
||||||
<textarea id="personaDetails" rows="3" placeholder="Personality traits, voice, quirks, goals"></textarea>
|
<textarea id="personaBackground" rows="3" placeholder="Backstory, history, role, or origins"></textarea>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="field field-wide">
|
||||||
|
<span>Details</span>
|
||||||
|
<textarea id="personaDetails" rows="4" placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="persona-actions">
|
<div class="persona-actions">
|
||||||
<button id="personaSave" type="submit" class="primary-action">Save persona</button>
|
<button id="personaSave" type="submit" class="primary-action">Save persona</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+19
-9
@@ -1,6 +1,6 @@
|
|||||||
const $ = (id) => document.getElementById(id);
|
const $ = (id) => document.getElementById(id);
|
||||||
const authDiv = $("auth"), chatDiv = $("chat"), authMsg = $("authMsg");
|
const authDiv = $("auth"), chatDiv = $("chat"), authMsg = $("authMsg");
|
||||||
const messagesDiv = $("messages"), emptyState = $("emptyState"), modelSelect = $("model");
|
const messagesDiv = $("messages"), modelSelect = $("model");
|
||||||
const loginForm = $("loginForm"), registerForm = $("registerForm"), msgInput = $("msgInput"), sendBtn = $("send");
|
const loginForm = $("loginForm"), registerForm = $("registerForm"), msgInput = $("msgInput"), sendBtn = $("send");
|
||||||
const loginUsernameInput = $("loginUsername"), loginPasswordInput = $("loginPassword");
|
const loginUsernameInput = $("loginUsername"), loginPasswordInput = $("loginPassword");
|
||||||
const registerUsernameInput = $("registerUsername"), registerPasswordInput = $("registerPassword");
|
const registerUsernameInput = $("registerUsername"), registerPasswordInput = $("registerPassword");
|
||||||
@@ -12,6 +12,7 @@ const personaList = $("personaList"), userPersonaList = $("userPersonaList");
|
|||||||
const personaForm = $("personaForm"), personaFormTitle = $("personaFormTitle"), personaTypeSelect = $("personaType");
|
const personaForm = $("personaForm"), personaFormTitle = $("personaFormTitle"), personaTypeSelect = $("personaType");
|
||||||
const personaNameInput = $("personaName"), personaPronounsInput = $("personaPronouns"), personaAppearanceInput = $("personaAppearance");
|
const personaNameInput = $("personaName"), personaPronounsInput = $("personaPronouns"), personaAppearanceInput = $("personaAppearance");
|
||||||
const personaBackgroundInput = $("personaBackground"), personaDetailsInput = $("personaDetails");
|
const personaBackgroundInput = $("personaBackground"), personaDetailsInput = $("personaDetails");
|
||||||
|
const personaFormNotice = $("personaFormNotice");
|
||||||
const newPersonaBtn = $("newPersona"), newUserPersonaBtn = $("newUserPersona"), clearPersonaBtn = $("clearPersona"), clearUserPersonaBtn = $("clearUserPersona");
|
const newPersonaBtn = $("newPersona"), newUserPersonaBtn = $("newUserPersona"), clearPersonaBtn = $("clearPersona"), clearUserPersonaBtn = $("clearUserPersona");
|
||||||
const activePersonaStatus = $("activePersonaStatus"), activeUserPersonaStatus = $("activeUserPersonaStatus");
|
const activePersonaStatus = $("activePersonaStatus"), activeUserPersonaStatus = $("activeUserPersonaStatus");
|
||||||
const personaModal = $("personaModal"), personaCloseBtn = $("personaClose"), personaMenuButton = $("personaMenuButton"), personaPopover = $("personaPopover");
|
const personaModal = $("personaModal"), personaCloseBtn = $("personaClose"), personaMenuButton = $("personaMenuButton"), personaPopover = $("personaPopover");
|
||||||
@@ -46,6 +47,16 @@ const del = (url) => request(url, null, "DELETE");
|
|||||||
|
|
||||||
function setAuthMessage(message, state = "") { authMsg.textContent = message; authMsg.className = state ? `status ${state}` : "status"; }
|
function setAuthMessage(message, state = "") { authMsg.textContent = message; authMsg.className = state ? `status ${state}` : "status"; }
|
||||||
function setNotice(message, state = "") { return {message, state}; }
|
function setNotice(message, state = "") { return {message, state}; }
|
||||||
|
function setPersonaFormNotice(message = "", state = "") {
|
||||||
|
if (!personaFormNotice) return;
|
||||||
|
if (!message) {
|
||||||
|
personaFormNotice.textContent = "";
|
||||||
|
personaFormNotice.className = "status hidden";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
personaFormNotice.textContent = message;
|
||||||
|
personaFormNotice.className = state ? `status ${state}` : "status";
|
||||||
|
}
|
||||||
function setMessageContent(el, content) {
|
function setMessageContent(el, content) {
|
||||||
if (window.marked && window.DOMPurify) {
|
if (window.marked && window.DOMPurify) {
|
||||||
el.innerHTML = DOMPurify.sanitize(marked.parse(content, {breaks: true}));
|
el.innerHTML = DOMPurify.sanitize(marked.parse(content, {breaks: true}));
|
||||||
@@ -53,7 +64,6 @@ function setMessageContent(el, content) {
|
|||||||
el.textContent = content;
|
el.textContent = content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function updateEmptyState() { emptyState.classList.toggle("visible", currentMessages.length === 0); }
|
|
||||||
function getChatById(id) { return chatSessions.find((chat) => chat.id === id); }
|
function getChatById(id) { return chatSessions.find((chat) => chat.id === id); }
|
||||||
|
|
||||||
function addMessage(content, isUser = false, isLoading = false) {
|
function addMessage(content, isUser = false, isLoading = false) {
|
||||||
@@ -87,7 +97,6 @@ function addMessage(content, isUser = false, isLoading = false) {
|
|||||||
function renderMessages() {
|
function renderMessages() {
|
||||||
messagesDiv.innerHTML = "";
|
messagesDiv.innerHTML = "";
|
||||||
currentMessages.forEach((msg) => addMessage(msg.content, msg.role === "user"));
|
currentMessages.forEach((msg) => addMessage(msg.content, msg.role === "user"));
|
||||||
updateEmptyState();
|
|
||||||
updateChatActionState();
|
updateChatActionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +187,11 @@ function openPersonaForm(persona = null, personaType = "assistant") {
|
|||||||
personaTypeSelect.value = personaType; personaTypeSelect.disabled = false;
|
personaTypeSelect.value = personaType; personaTypeSelect.disabled = false;
|
||||||
personaNameInput.value = ""; personaPronounsInput.value = ""; personaAppearanceInput.value = ""; personaBackgroundInput.value = ""; personaDetailsInput.value = "";
|
personaNameInput.value = ""; personaPronounsInput.value = ""; personaAppearanceInput.value = ""; personaBackgroundInput.value = ""; personaDetailsInput.value = "";
|
||||||
}
|
}
|
||||||
|
setPersonaFormNotice("");
|
||||||
personaNameInput.focus();
|
personaNameInput.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function closePersonaForm() { personaModal.classList.add("hidden"); editingPersonaId = null; editingPersonaType = "assistant"; personaTypeSelect.disabled = false; }
|
function closePersonaForm() { personaModal.classList.add("hidden"); editingPersonaId = null; editingPersonaType = "assistant"; personaTypeSelect.disabled = false; setPersonaFormNotice(""); }
|
||||||
function closePersonaPopover() { personaPopover.classList.add("hidden"); personaMenuButton.setAttribute("aria-expanded", "false"); }
|
function closePersonaPopover() { personaPopover.classList.add("hidden"); personaMenuButton.setAttribute("aria-expanded", "false"); }
|
||||||
|
|
||||||
function renderPersonaList(items, activeId, listElement, personaType) {
|
function renderPersonaList(items, activeId, listElement, personaType) {
|
||||||
@@ -252,9 +262,9 @@ async function clearPersona(type) {
|
|||||||
|
|
||||||
async function savePersona() {
|
async function savePersona() {
|
||||||
const payload = {personaType: personaTypeSelect.value, name: personaNameInput.value.trim(), pronouns: personaPronounsInput.value.trim(), appearance: personaAppearanceInput.value.trim(), background: personaBackgroundInput.value.trim(), details: personaDetailsInput.value.trim()};
|
const payload = {personaType: personaTypeSelect.value, name: personaNameInput.value.trim(), pronouns: personaPronounsInput.value.trim(), appearance: personaAppearanceInput.value.trim(), background: personaBackgroundInput.value.trim(), details: personaDetailsInput.value.trim()};
|
||||||
if (!payload.name) return setNotice("Persona name is required.", "error");
|
if (!payload.name) return setPersonaFormNotice("Persona name is required.", "error");
|
||||||
const res = editingPersonaId ? await put(`/personas/${editingPersonaId}`, payload) : await post("/personas", payload);
|
const res = editingPersonaId ? await put(`/personas/${editingPersonaId}`, payload) : await post("/personas", payload);
|
||||||
if (res.error) return setNotice(res.error, "error");
|
if (res.error) return setPersonaFormNotice(res.error, "error");
|
||||||
const wasEditing = Boolean(editingPersonaId);
|
const wasEditing = Boolean(editingPersonaId);
|
||||||
await loadPersonas(); closePersonaForm(); setNotice(wasEditing ? "Persona updated." : "Persona created.", "success");
|
await loadPersonas(); closePersonaForm(); setNotice(wasEditing ? "Persona updated." : "Persona created.", "success");
|
||||||
}
|
}
|
||||||
@@ -391,7 +401,7 @@ async function sendMessage() {
|
|||||||
const autoTitle = message.length > 40 ? `${message.slice(0, 40)}...` : message;
|
const autoTitle = message.length > 40 ? `${message.slice(0, 40)}...` : message;
|
||||||
currentChat.title = autoTitle; updateWorkspaceCopy(); renderChatList(); await put(`/chats/${activeChatId}`, {title: autoTitle});
|
currentChat.title = autoTitle; updateWorkspaceCopy(); renderChatList(); await put(`/chats/${activeChatId}`, {title: autoTitle});
|
||||||
}
|
}
|
||||||
currentMessages.push({role: "user", content: message}); addMessage(message, true); updateEmptyState(); updateChatActionState();
|
currentMessages.push({role: "user", content: message}); addMessage(message, true); updateChatActionState();
|
||||||
msgInput.value = ""; msgInput.style.height = "auto"; setLoadingState(true); setNotice("Generating reply...");
|
msgInput.value = ""; msgInput.style.height = "auto"; setLoadingState(true); setNotice("Generating reply...");
|
||||||
const loadingMsg = addMessage("", false, true);
|
const loadingMsg = addMessage("", false, true);
|
||||||
try {
|
try {
|
||||||
@@ -423,7 +433,7 @@ registerForm.addEventListener("submit", (event) => { event.preventDefault(); han
|
|||||||
$("logout").addEventListener("click", async () => {
|
$("logout").addEventListener("click", async () => {
|
||||||
await post("/logout", {}); chatDiv.style.display = "none"; authDiv.style.display = "grid";
|
await post("/logout", {}); chatDiv.style.display = "none"; authDiv.style.display = "grid";
|
||||||
activeChatId = null; currentUsername = ""; currentSummary = null; chatSessions = []; currentMessages = []; assistantPersonas = []; userPersonas = [];
|
activeChatId = null; currentUsername = ""; currentSummary = null; chatSessions = []; currentMessages = []; assistantPersonas = []; userPersonas = [];
|
||||||
activePersonaId = null; activeUserPersonaId = null; publishedPersonaIds = new Set(); messagesDiv.innerHTML = ""; renderChatList(); updateEmptyState(); updateChatActionState();
|
activePersonaId = null; activeUserPersonaId = null; publishedPersonaIds = new Set(); messagesDiv.innerHTML = ""; renderChatList(); updateChatActionState();
|
||||||
showAuthScreen("login"); setAuthMessage("Logged out.", "success"); setNotice("Ready.");
|
showAuthScreen("login"); setAuthMessage("Logged out.", "success"); setNotice("Ready.");
|
||||||
});
|
});
|
||||||
sendBtn.addEventListener("click", sendMessage);
|
sendBtn.addEventListener("click", sendMessage);
|
||||||
@@ -440,5 +450,5 @@ personaMenuButton.addEventListener("click", togglePersonaPopover); personaPopove
|
|||||||
document.addEventListener("keydown", (event) => { if (event.key === "Escape") { closePersonaPopover(); closePersonaForm(); } });
|
document.addEventListener("keydown", (event) => { if (event.key === "Escape") { closePersonaPopover(); closePersonaForm(); } });
|
||||||
window.addEventListener("load", async () => {
|
window.addEventListener("load", async () => {
|
||||||
applyTheme(localStorage.getItem("krishd-theme") || "fakegpt", false);
|
applyTheme(localStorage.getItem("krishd-theme") || "fakegpt", false);
|
||||||
setNotice("Ready."); updateEmptyState(); updateChatActionState(); await checkSession();
|
setNotice("Ready."); updateChatActionState(); await checkSession();
|
||||||
});
|
});
|
||||||
|
|||||||
+96
-25
@@ -334,8 +334,7 @@ h2 {
|
|||||||
.subtitle,
|
.subtitle,
|
||||||
.persona-guidance,
|
.persona-guidance,
|
||||||
.persona-helper,
|
.persona-helper,
|
||||||
.composer-hint,
|
.composer-hint {
|
||||||
.empty-state p:last-child {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-soft);
|
color: var(--text-soft);
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
@@ -390,7 +389,6 @@ h2 {
|
|||||||
.input-shell,
|
.input-shell,
|
||||||
.chat-sidebar,
|
.chat-sidebar,
|
||||||
.messages-panel,
|
.messages-panel,
|
||||||
.empty-state,
|
|
||||||
.persona-popover,
|
.persona-popover,
|
||||||
.modal-card,
|
.modal-card,
|
||||||
.market-shell {
|
.market-shell {
|
||||||
@@ -758,7 +756,6 @@ select:focus {
|
|||||||
|
|
||||||
.workspace-overview,
|
.workspace-overview,
|
||||||
.messages-panel,
|
.messages-panel,
|
||||||
.empty-state,
|
|
||||||
.input-shell {
|
.input-shell {
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
@@ -863,18 +860,6 @@ select:focus {
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
display: none;
|
|
||||||
align-content: center;
|
|
||||||
justify-items: start;
|
|
||||||
gap: 8px;
|
|
||||||
background: rgba(250, 244, 235, 0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state.visible {
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msg {
|
.msg {
|
||||||
max-width: min(78%, 780px);
|
max-width: min(78%, 780px);
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
@@ -1175,22 +1160,83 @@ select:focus {
|
|||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background: rgba(24, 32, 31, 0.16);
|
background: rgba(12, 16, 18, 0.3);
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(10px);
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-card {
|
.modal-card {
|
||||||
width: min(560px, 95vw);
|
width: min(760px, 95vw);
|
||||||
|
max-height: calc(100vh - 48px);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
padding: 18px;
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.persona-form {
|
.persona-form {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 18px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-modal-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-modal-heading {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-form-intro {
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(24, 32, 31, 0.04);
|
||||||
|
border: 1px solid rgba(24, 32, 31, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-form > .status {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-wide {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-form .field,
|
||||||
|
.persona-form .field-wide {
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
|
background: rgba(24, 32, 31, 0.03);
|
||||||
|
border: 1px solid rgba(24, 32, 31, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-form .field textarea {
|
||||||
|
min-height: 112px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-actions .primary-action {
|
||||||
|
width: auto;
|
||||||
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.market-shell {
|
.market-shell {
|
||||||
@@ -1432,7 +1478,9 @@ body[data-theme="fakegpt"] .chat-item button,
|
|||||||
body[data-theme="fakegpt"] .session-chip,
|
body[data-theme="fakegpt"] .session-chip,
|
||||||
body[data-theme="fakegpt"] .settings-nav,
|
body[data-theme="fakegpt"] .settings-nav,
|
||||||
body[data-theme="fakegpt"] .settings-nav-item:not(.active),
|
body[data-theme="fakegpt"] .settings-nav-item:not(.active),
|
||||||
body[data-theme="fakegpt"] .settings-theme-note {
|
body[data-theme="fakegpt"] .settings-theme-note,
|
||||||
|
body[data-theme="fakegpt"] .persona-form-intro,
|
||||||
|
body[data-theme="fakegpt"] .persona-form .field {
|
||||||
background: rgba(18, 24, 21, 0.96);
|
background: rgba(18, 24, 21, 0.96);
|
||||||
color: #d7fff3;
|
color: #d7fff3;
|
||||||
border: 1px solid rgba(36, 255, 180, 0.16);
|
border: 1px solid rgba(36, 255, 180, 0.16);
|
||||||
@@ -1515,7 +1563,9 @@ body[data-theme="fraud"] .icon-button,
|
|||||||
body[data-theme="fraud"] .session-chip,
|
body[data-theme="fraud"] .session-chip,
|
||||||
body[data-theme="fraud"] .settings-nav,
|
body[data-theme="fraud"] .settings-nav,
|
||||||
body[data-theme="fraud"] .settings-nav-item:not(.active),
|
body[data-theme="fraud"] .settings-nav-item:not(.active),
|
||||||
body[data-theme="fraud"] .settings-theme-note {
|
body[data-theme="fraud"] .settings-theme-note,
|
||||||
|
body[data-theme="fraud"] .persona-form-intro,
|
||||||
|
body[data-theme="fraud"] .persona-form .field {
|
||||||
background: rgba(255, 249, 242, 0.88);
|
background: rgba(255, 249, 242, 0.88);
|
||||||
color: #4b3d31;
|
color: #4b3d31;
|
||||||
border: 1px solid rgba(84, 62, 43, 0.08);
|
border: 1px solid rgba(84, 62, 43, 0.08);
|
||||||
@@ -1628,7 +1678,9 @@ body[data-theme="slopilot"] .session-chip {
|
|||||||
|
|
||||||
body[data-theme="slopilot"] .settings-nav,
|
body[data-theme="slopilot"] .settings-nav,
|
||||||
body[data-theme="slopilot"] .settings-nav-item:not(.active),
|
body[data-theme="slopilot"] .settings-nav-item:not(.active),
|
||||||
body[data-theme="slopilot"] .settings-theme-note {
|
body[data-theme="slopilot"] .settings-theme-note,
|
||||||
|
body[data-theme="slopilot"] .persona-form-intro,
|
||||||
|
body[data-theme="slopilot"] .persona-form .field {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-color: rgba(13, 104, 255, 0.12);
|
border-color: rgba(13, 104, 255, 0.12);
|
||||||
}
|
}
|
||||||
@@ -1714,7 +1766,9 @@ body[data-theme="confusity"] .session-chip {
|
|||||||
|
|
||||||
body[data-theme="confusity"] .settings-nav,
|
body[data-theme="confusity"] .settings-nav,
|
||||||
body[data-theme="confusity"] .settings-nav-item:not(.active),
|
body[data-theme="confusity"] .settings-nav-item:not(.active),
|
||||||
body[data-theme="confusity"] .settings-theme-note {
|
body[data-theme="confusity"] .settings-theme-note,
|
||||||
|
body[data-theme="confusity"] .persona-form-intro,
|
||||||
|
body[data-theme="confusity"] .persona-form .field {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-color: rgba(17, 24, 39, 0.08);
|
border-color: rgba(17, 24, 39, 0.08);
|
||||||
}
|
}
|
||||||
@@ -1795,6 +1849,19 @@ body[data-theme="confusity"] .settings-nav-item.active {
|
|||||||
border: 1px solid rgba(17, 24, 39, 0.16);
|
border: 1px solid rgba(17, 24, 39, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme="germini"] .persona-form-intro,
|
||||||
|
body[data-theme="germini"] .persona-form .field {
|
||||||
|
background: rgba(255, 255, 255, 0.26);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="beta-ai"] .persona-form-intro,
|
||||||
|
body[data-theme="beta-ai"] .persona-form .field {
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(243, 238, 255, 0.92));
|
||||||
|
border: 1px solid rgba(90, 103, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes gemFloat {
|
@keyframes gemFloat {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
@@ -1923,6 +1990,10 @@ body[data-theme="confusity"] .settings-nav-item.active {
|
|||||||
justify-self: stretch;
|
justify-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.persona-form-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-shell .market-header-simple {
|
.settings-shell .market-header-simple {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -782,6 +782,56 @@ const normalizePersonaField = (value) => {
|
|||||||
return trimmed ? trimmed : null;
|
return trimmed ? trimmed : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PERSONA_FIELD_LIMITS = {
|
||||||
|
name: 80,
|
||||||
|
pronouns: 40,
|
||||||
|
appearance: 400,
|
||||||
|
background: 1000,
|
||||||
|
details: 1200
|
||||||
|
};
|
||||||
|
|
||||||
|
const blockedPersonaPatterns = [
|
||||||
|
/\b(child|children|kid|kids|minor|underage|under-aged|teenager|young girl|young boy|loli|shota)\b/i,
|
||||||
|
/\b(nazi|hitler|third reich|kkk|ku klux klan|white power|nigger|nigga)\b/i,
|
||||||
|
/\b(rape|rapist|sexual assault|molest|molestation|incest|bestiality|zoophilia|necrophilia)\b/i,
|
||||||
|
/\b(self-harm|self harm|suicide fetish)\b/i
|
||||||
|
];
|
||||||
|
|
||||||
|
const validatePersonaPayload = (payload) => {
|
||||||
|
const sanitized = {
|
||||||
|
name: (payload?.name || "").trim(),
|
||||||
|
pronouns: normalizePersonaField(payload?.pronouns),
|
||||||
|
appearance: normalizePersonaField(payload?.appearance),
|
||||||
|
background: normalizePersonaField(payload?.background),
|
||||||
|
details: normalizePersonaField(payload?.details)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!sanitized.name) {
|
||||||
|
return {error: "Name is required"};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, limit] of Object.entries(PERSONA_FIELD_LIMITS)) {
|
||||||
|
const value = sanitized[key];
|
||||||
|
if (value && value.length > limit) {
|
||||||
|
return {error: `${key.charAt(0).toUpperCase() + key.slice(1)} is too long`};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const combinedText = [
|
||||||
|
sanitized.name,
|
||||||
|
sanitized.pronouns,
|
||||||
|
sanitized.appearance,
|
||||||
|
sanitized.background,
|
||||||
|
sanitized.details
|
||||||
|
].filter(Boolean).join("\n");
|
||||||
|
|
||||||
|
if (blockedPersonaPatterns.some((pattern) => pattern.test(combinedText))) {
|
||||||
|
return {error: "This persona contains disallowed content. Remove sexual content involving minors, extreme sexual violence, or hateful/extremist material."};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {sanitized};
|
||||||
|
};
|
||||||
|
|
||||||
const buildPersonaPrompt = (persona) => {
|
const buildPersonaPrompt = (persona) => {
|
||||||
const lines = [
|
const lines = [
|
||||||
"SYSTEM ROLE:",
|
"SYSTEM ROLE:",
|
||||||
@@ -921,18 +971,15 @@ app.get("/personas", requireLogin, (req, res) => {
|
|||||||
|
|
||||||
app.post("/personas", requireLogin, (req, res) => {
|
app.post("/personas", requireLogin, (req, res) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
const name = (req.body?.name || "").trim();
|
|
||||||
if (!name) {
|
|
||||||
return res.status(400).json({error: "Name is required"});
|
|
||||||
}
|
|
||||||
const personaType = (req.body?.personaType || "assistant").trim();
|
const personaType = (req.body?.personaType || "assistant").trim();
|
||||||
if (!["assistant", "user"].includes(personaType)) {
|
if (!["assistant", "user"].includes(personaType)) {
|
||||||
return res.status(400).json({error: "Invalid persona type"});
|
return res.status(400).json({error: "Invalid persona type"});
|
||||||
}
|
}
|
||||||
const pronouns = normalizePersonaField(req.body?.pronouns);
|
const validation = validatePersonaPayload(req.body);
|
||||||
const appearance = normalizePersonaField(req.body?.appearance);
|
if (validation.error) {
|
||||||
const background = normalizePersonaField(req.body?.background);
|
return res.status(400).json({error: validation.error});
|
||||||
const details = normalizePersonaField(req.body?.details);
|
}
|
||||||
|
const {name, pronouns, appearance, background, details} = validation.sanitized;
|
||||||
const personaId = insertPersonaStmt.run(
|
const personaId = insertPersonaStmt.run(
|
||||||
user,
|
user,
|
||||||
name,
|
name,
|
||||||
@@ -949,14 +996,11 @@ app.post("/personas", requireLogin, (req, res) => {
|
|||||||
app.put("/personas/:id", requireLogin, (req, res) => {
|
app.put("/personas/:id", requireLogin, (req, res) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
const personaId = Number(req.params.id);
|
const personaId = Number(req.params.id);
|
||||||
const name = (req.body?.name || "").trim();
|
const validation = validatePersonaPayload(req.body);
|
||||||
if (!name) {
|
if (validation.error) {
|
||||||
return res.status(400).json({error: "Name is required"});
|
return res.status(400).json({error: validation.error});
|
||||||
}
|
}
|
||||||
const pronouns = normalizePersonaField(req.body?.pronouns);
|
const {name, pronouns, appearance, background, details} = validation.sanitized;
|
||||||
const appearance = normalizePersonaField(req.body?.appearance);
|
|
||||||
const background = normalizePersonaField(req.body?.background);
|
|
||||||
const details = normalizePersonaField(req.body?.details);
|
|
||||||
const result = updatePersonaStmt.run(
|
const result = updatePersonaStmt.run(
|
||||||
name,
|
name,
|
||||||
pronouns,
|
pronouns,
|
||||||
|
|||||||
Reference in New Issue
Block a user