Merge branch 'main' into add-user-persona-creation-feature

This commit is contained in:
2026-01-07 21:08:29 +01:00
committed by GitHub
4 changed files with 49 additions and 207 deletions
+28
View File
@@ -88,6 +88,34 @@
</div>
<p id="activePersonaStatus" class="status persona-status">No persona equipped.</p>
<div id="personaList" class="persona-list"></div>
<form id="personaForm" class="persona-form hidden">
<h3 id="personaFormTitle">Create persona</h3>
<label class="field">
<span>Name</span>
<input id="personaName" placeholder="Persona name" required/>
</label>
<label class="field">
<span>Pronouns</span>
<input id="personaPronouns" placeholder="e.g. she/her, they/them"/>
</label>
<label class="field">
<span>Appearance</span>
<textarea id="personaAppearance" rows="2" placeholder="Describe their look"></textarea>
</label>
<label class="field">
<span>Background</span>
<textarea id="personaBackground" rows="2" placeholder="Backstory or origins"></textarea>
</label>
<label class="field">
<span>Details</span>
<textarea id="personaDetails" rows="3"
placeholder="Personality traits, voice, or quirks"></textarea>
</label>
<div class="persona-actions">
<button id="personaSave" type="submit" class="primary-action">Save persona</button>
<button id="personaCancel" type="button" class="secondary-action">Cancel</button>
</div>
</form>
</div>
</aside>
<section class="chat-main">
+4 -74
View File
@@ -28,10 +28,6 @@ const personaPronounsInput = document.getElementById("personaPronouns");
const personaAppearanceInput = document.getElementById("personaAppearance");
const personaBackgroundInput = document.getElementById("personaBackground");
const personaDetailsInput = document.getElementById("personaDetails");
const personaModal = document.getElementById("personaModal");
const personaCloseBtn = document.getElementById("personaClose");
const addCustomFieldBtn = document.getElementById("addCustomField");
const customFieldsList = document.getElementById("customFieldsList");
const newPersonaBtn = document.getElementById("newPersona");
const clearPersonaBtn = document.getElementById("clearPersona");
const personaCancelBtn = document.getElementById("personaCancel");
@@ -198,7 +194,7 @@ function setActivePersonaStatus() {
}
function openPersonaForm(persona = null) {
personaModal.classList.remove('hidden');
personaForm.classList.remove('hidden');
if (persona) {
editingPersonaId = persona.id;
personaFormTitle.textContent = 'Edit persona';
@@ -207,7 +203,6 @@ function openPersonaForm(persona = null) {
personaAppearanceInput.value = persona.appearance || '';
personaBackgroundInput.value = persona.background || '';
personaDetailsInput.value = persona.details || '';
renderCustomFields(persona.customFields || []);
} else {
editingPersonaId = null;
personaFormTitle.textContent = 'Create persona';
@@ -216,60 +211,15 @@ function openPersonaForm(persona = null) {
personaAppearanceInput.value = '';
personaBackgroundInput.value = '';
personaDetailsInput.value = '';
renderCustomFields([]);
}
personaNameInput.focus();
}
function closePersonaForm() {
personaModal.classList.add('hidden');
personaForm.classList.add('hidden');
editingPersonaId = null;
}
function renderCustomFields(fields) {
customFieldsList.innerHTML = '';
fields.forEach((field, index) => {
addCustomFieldRow(field.label || '', field.value || '', index);
});
}
function addCustomFieldRow(label = '', value = '') {
const row = document.createElement('div');
row.className = 'custom-field-row';
const labelInput = document.createElement('input');
labelInput.type = 'text';
labelInput.placeholder = 'Label';
labelInput.value = label;
const valueInput = document.createElement('input');
valueInput.type = 'text';
valueInput.placeholder = 'Value';
valueInput.value = value;
const removeBtn = document.createElement('button');
removeBtn.type = 'button';
removeBtn.textContent = 'Remove';
removeBtn.className = 'secondary-action';
removeBtn.addEventListener('click', () => row.remove());
row.appendChild(labelInput);
row.appendChild(valueInput);
row.appendChild(removeBtn);
customFieldsList.appendChild(row);
}
function collectCustomFields() {
const rows = customFieldsList.querySelectorAll('.custom-field-row');
return Array.from(rows).map(row => {
const [labelInput, valueInput] = row.querySelectorAll('input');
return {
label: labelInput.value.trim(),
value: valueInput.value.trim()
};
}).filter(field => field.label || field.value);
}
function renderPersonaList() {
personaList.innerHTML = '';
if (!personas.length) {
@@ -321,10 +271,7 @@ function renderPersonaList() {
async function loadPersonas() {
const res = await get('/personas');
if (res.error) return;
personas = (res.personas || []).map(persona => ({
...persona,
customFields: persona.customFields || []
}));
personas = res.personas || [];
activePersonaId = res.activePersonaId || null;
renderPersonaList();
}
@@ -349,8 +296,7 @@ async function savePersona() {
pronouns: personaPronounsInput.value.trim(),
appearance: personaAppearanceInput.value.trim(),
background: personaBackgroundInput.value.trim(),
details: personaDetailsInput.value.trim(),
customFields: collectCustomFields()
details: personaDetailsInput.value.trim()
};
if (!payload.name) return;
let res;
@@ -374,12 +320,6 @@ async function deletePersona(personaId) {
await loadPersonas();
}
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !personaModal.classList.contains('hidden')) {
closePersonaForm();
}
});
function setLoadingState(loading) {
isProcessing = loading;
sendBtn.disabled = loading;
@@ -650,16 +590,6 @@ newPersonaBtn.addEventListener('click', () => openPersonaForm());
personaCancelBtn.addEventListener('click', () => closePersonaForm());
personaCloseBtn.addEventListener('click', () => closePersonaForm());
personaModal.addEventListener('click', (event) => {
if (event.target === personaModal) {
closePersonaForm();
}
});
addCustomFieldBtn.addEventListener('click', () => addCustomFieldRow());
clearPersonaBtn.addEventListener('click', () => clearPersona());
personaForm.addEventListener('submit', (event) => {
+8 -80
View File
@@ -461,9 +461,14 @@ button:disabled {
.persona-form {
display: grid;
gap: 10px;
max-height: 65vh;
overflow-y: auto;
padding-right: 6px;
background: rgba(10, 10, 20, 0.75);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
padding: 12px;
}
.persona-form.hidden {
display: none;
}
.persona-form h3 {
@@ -477,83 +482,6 @@ button:disabled {
flex-wrap: wrap;
}
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(5, 5, 20, 0.75);
display: grid;
place-items: center;
z-index: 1000;
padding: 24px;
}
.modal-overlay.hidden {
display: none;
}
.modal-card {
width: min(520px, 92vw);
background: rgba(19, 19, 42, 0.96);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 22px;
padding: 18px;
box-shadow: 0 25px 60px -12px var(--shadow);
display: grid;
gap: 14px;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.modal-header h3 {
margin: 0;
font-size: 1.2rem;
}
.custom-fields {
display: grid;
gap: 10px;
background: rgba(10, 10, 20, 0.6);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 14px;
padding: 10px;
}
.custom-fields-header {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.9rem;
color: var(--text-secondary);
}
.custom-fields-list {
display: grid;
gap: 8px;
}
.custom-field-row {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 8px;
align-items: center;
}
.custom-field-row input {
padding: 10px 12px;
font-size: 14px;
}
.custom-field-row button {
padding: 8px 10px;
font-size: 12px;
border-radius: 10px;
}
.chat-main {
display: grid;
grid-template-rows: auto 1fr auto auto;