Merge pull request #9 from Fretuks/move-market-section-to-separate-page
Move Persona Market to its own page and clarify persona UI/prompts
This commit is contained in:
+4
-21
@@ -89,6 +89,7 @@
|
|||||||
<option disabled selected>Loading models...</option>
|
<option disabled selected>Loading models...</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="/market.html" class="secondary-action market-link">Persona Market</a>
|
||||||
<div class="persona-menu">
|
<div class="persona-menu">
|
||||||
<button id="personaMenuButton" class="secondary-action" type="button"
|
<button id="personaMenuButton" class="secondary-action" type="button"
|
||||||
aria-expanded="false" aria-controls="personaPopover">
|
aria-expanded="false" aria-controls="personaPopover">
|
||||||
@@ -100,6 +101,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">Roleplay</p>
|
<p class="eyebrow">Roleplay</p>
|
||||||
<h2>AI Persona</h2>
|
<h2>AI Persona</h2>
|
||||||
|
<p class="persona-guidance">Controls how the assistant sounds and behaves.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="persona-section-actions">
|
<div class="persona-section-actions">
|
||||||
<button id="newPersona" class="secondary-action" type="button">New AI</button>
|
<button id="newPersona" class="secondary-action" type="button">New AI</button>
|
||||||
@@ -114,6 +116,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">Roleplay</p>
|
<p class="eyebrow">Roleplay</p>
|
||||||
<h2>Your Persona</h2>
|
<h2>Your Persona</h2>
|
||||||
|
<p class="persona-guidance">Defines how the assistant should portray you.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="persona-section-actions">
|
<div class="persona-section-actions">
|
||||||
<button id="newUserPersona" class="secondary-action" type="button">New you</button>
|
<button id="newUserPersona" class="secondary-action" type="button">New you</button>
|
||||||
@@ -123,27 +126,6 @@
|
|||||||
<p id="activeUserPersonaStatus" class="status persona-status">No user persona set.</p>
|
<p id="activeUserPersonaStatus" class="status persona-status">No user persona set.</p>
|
||||||
<div id="userPersonaList" class="persona-list"></div>
|
<div id="userPersonaList" class="persona-list"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="persona-section">
|
|
||||||
<div class="persona-section-header">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Community</p>
|
|
||||||
<h2>Persona Market</h2>
|
|
||||||
</div>
|
|
||||||
<div class="persona-section-actions">
|
|
||||||
<button id="refreshMarket" class="secondary-action" type="button">Refresh</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="persona-market-columns">
|
|
||||||
<div class="persona-market-column">
|
|
||||||
<h3>AI personas</h3>
|
|
||||||
<div id="marketPersonaList" class="persona-list"></div>
|
|
||||||
</div>
|
|
||||||
<div class="persona-market-column">
|
|
||||||
<h3>User personas</h3>
|
|
||||||
<div id="marketUserPersonaList" class="persona-list"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -175,6 +157,7 @@
|
|||||||
<option value="user">Your persona</option>
|
<option value="user">Your persona</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<p class="persona-helper">AI personas shape how the assistant speaks. Your persona describes how you should be portrayed.</p>
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<span>Name</span>
|
<span>Name</span>
|
||||||
<input id="personaName" placeholder="Persona name" required/>
|
<input id="personaName" placeholder="Persona name" required/>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<title>Persona Market</title>
|
||||||
|
<link rel="stylesheet" href="style.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="market-shell card">
|
||||||
|
<header class="market-header">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Community</p>
|
||||||
|
<h1>Persona Market</h1>
|
||||||
|
<p class="subtitle">Browse shared personas and choose who should embody them.</p>
|
||||||
|
</div>
|
||||||
|
<div class="market-header-actions">
|
||||||
|
<button id="refreshMarket" class="secondary-action" type="button">Refresh listings</button>
|
||||||
|
<a href="/" class="secondary-action">Back to chat</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<p id="marketStatus" class="status persona-status">Loading market personas...</p>
|
||||||
|
<div class="persona-market-columns market-page-columns">
|
||||||
|
<div class="persona-market-column">
|
||||||
|
<h3>AI personas</h3>
|
||||||
|
<p class="persona-guidance">These shape how the assistant speaks and behaves.</p>
|
||||||
|
<div id="marketPersonaList" class="persona-list"></div>
|
||||||
|
</div>
|
||||||
|
<div class="persona-market-column">
|
||||||
|
<h3>User personas</h3>
|
||||||
|
<p class="persona-guidance">These describe how the assistant should portray you.</p>
|
||||||
|
<div id="marketUserPersonaList" class="persona-list"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="market.js" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
const marketPersonaList = document.getElementById("marketPersonaList");
|
||||||
|
const marketUserPersonaList = document.getElementById("marketUserPersonaList");
|
||||||
|
const refreshMarketBtn = document.getElementById("refreshMarket");
|
||||||
|
const marketStatus = document.getElementById("marketStatus");
|
||||||
|
|
||||||
|
async function request(url, data, method = "POST") {
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method,
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: data ? JSON.stringify(data) : undefined
|
||||||
|
});
|
||||||
|
return await res.json();
|
||||||
|
} catch (err) {
|
||||||
|
return {error: "Network error — please try again."};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function post(url, data) {
|
||||||
|
return request(url, data, "POST");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get(url) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(url);
|
||||||
|
return await res.json();
|
||||||
|
} catch (err) {
|
||||||
|
return {error: "Network error — please try again."};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMarketStatus(message, state = "") {
|
||||||
|
if (!marketStatus) return;
|
||||||
|
marketStatus.textContent = message;
|
||||||
|
marketStatus.className = state ? `status persona-status ${state}` : "status persona-status";
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPersonaMeta(persona) {
|
||||||
|
const pronouns = persona.pronouns ? `Pronouns: ${persona.pronouns}` : "Pronouns: —";
|
||||||
|
return `By ${persona.creator_username} • ${pronouns}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarketList(personaItems, listElement, personaType) {
|
||||||
|
if (!listElement) return;
|
||||||
|
listElement.innerHTML = "";
|
||||||
|
if (!personaItems.length) {
|
||||||
|
const empty = document.createElement("p");
|
||||||
|
empty.className = "status persona-status";
|
||||||
|
empty.textContent = personaType === "assistant"
|
||||||
|
? "No AI personas listed yet. Publish the first one!"
|
||||||
|
: "No user personas listed yet. Publish the first one!";
|
||||||
|
listElement.appendChild(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
personaItems.forEach(persona => {
|
||||||
|
const item = document.createElement("div");
|
||||||
|
item.className = "persona-item";
|
||||||
|
|
||||||
|
const titleRow = document.createElement("div");
|
||||||
|
titleRow.className = "persona-title-row";
|
||||||
|
|
||||||
|
const title = document.createElement("h4");
|
||||||
|
title.textContent = persona.name;
|
||||||
|
|
||||||
|
const roleTag = document.createElement("span");
|
||||||
|
roleTag.className = `persona-role-tag ${personaType === "assistant" ? "ai" : "user"}`;
|
||||||
|
roleTag.textContent = personaType === "assistant" ? "AI" : "You";
|
||||||
|
|
||||||
|
titleRow.appendChild(title);
|
||||||
|
titleRow.appendChild(roleTag);
|
||||||
|
|
||||||
|
const meta = document.createElement("p");
|
||||||
|
meta.textContent = buildPersonaMeta(persona);
|
||||||
|
|
||||||
|
const actions = document.createElement("div");
|
||||||
|
actions.className = "persona-item-actions";
|
||||||
|
|
||||||
|
const getBtn = document.createElement("button");
|
||||||
|
getBtn.type = "button";
|
||||||
|
getBtn.textContent = personaType === "assistant" ? "Get & equip as AI" : "Get & equip as You";
|
||||||
|
getBtn.addEventListener("click", () => collectMarketPersona(persona.id, persona.persona_type));
|
||||||
|
|
||||||
|
actions.appendChild(getBtn);
|
||||||
|
item.appendChild(titleRow);
|
||||||
|
item.appendChild(meta);
|
||||||
|
item.appendChild(actions);
|
||||||
|
listElement.appendChild(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMarketPersonas() {
|
||||||
|
setMarketStatus("Loading market personas...");
|
||||||
|
const res = await get("/personas/market");
|
||||||
|
if (res.error) {
|
||||||
|
setMarketStatus(res.error, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const marketPersonas = res.personas || [];
|
||||||
|
const assistantMarket = marketPersonas.filter(persona => persona.persona_type === "assistant");
|
||||||
|
const userMarket = marketPersonas.filter(persona => persona.persona_type === "user");
|
||||||
|
renderMarketList(assistantMarket, marketPersonaList, "assistant");
|
||||||
|
renderMarketList(userMarket, marketUserPersonaList, "user");
|
||||||
|
setMarketStatus("Choose an AI persona or a user persona to equip.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectMarketPersona(marketId, personaType) {
|
||||||
|
setMarketStatus(
|
||||||
|
personaType === "assistant"
|
||||||
|
? "Equipping the AI persona..."
|
||||||
|
: "Equipping the user persona..."
|
||||||
|
);
|
||||||
|
const res = await post(`/personas/market/${marketId}/collect`, {equip: true});
|
||||||
|
if (res.error) {
|
||||||
|
setMarketStatus(res.error, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setMarketStatus(
|
||||||
|
personaType === "assistant"
|
||||||
|
? "AI persona equipped. Head back to chat to use it."
|
||||||
|
: "User persona equipped. Head back to chat to use it.",
|
||||||
|
"success"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshMarketBtn) {
|
||||||
|
refreshMarketBtn.addEventListener("click", () => loadMarketPersonas());
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
loadMarketPersonas();
|
||||||
|
});
|
||||||
+13
-69
@@ -41,9 +41,6 @@ const personaModal = document.getElementById("personaModal");
|
|||||||
const personaCloseBtn = document.getElementById("personaClose");
|
const personaCloseBtn = document.getElementById("personaClose");
|
||||||
const personaMenuButton = document.getElementById("personaMenuButton");
|
const personaMenuButton = document.getElementById("personaMenuButton");
|
||||||
const personaPopover = document.getElementById("personaPopover");
|
const personaPopover = document.getElementById("personaPopover");
|
||||||
const marketPersonaList = document.getElementById("marketPersonaList");
|
|
||||||
const marketUserPersonaList = document.getElementById("marketUserPersonaList");
|
|
||||||
const refreshMarketBtn = document.getElementById("refreshMarket");
|
|
||||||
|
|
||||||
let isProcessing = false;
|
let isProcessing = false;
|
||||||
let chatSessions = [];
|
let chatSessions = [];
|
||||||
@@ -54,7 +51,6 @@ let activePersonaId = null;
|
|||||||
let activeUserPersonaId = null;
|
let activeUserPersonaId = null;
|
||||||
let editingPersonaId = null;
|
let editingPersonaId = null;
|
||||||
let editingPersonaType = "assistant";
|
let editingPersonaType = "assistant";
|
||||||
let marketPersonas = [];
|
|
||||||
let publishedPersonaIds = new Set();
|
let publishedPersonaIds = new Set();
|
||||||
|
|
||||||
const displayModels = async () => {
|
const displayModels = async () => {
|
||||||
@@ -214,15 +210,15 @@ function setActivePersonaStatus() {
|
|||||||
assistantPersonas,
|
assistantPersonas,
|
||||||
activePersonaId,
|
activePersonaId,
|
||||||
activePersonaStatus,
|
activePersonaStatus,
|
||||||
'No persona equipped.',
|
'No AI persona equipped.',
|
||||||
'AI equipped'
|
'AI persona equipped'
|
||||||
);
|
);
|
||||||
updateActivePersonaStatus(
|
updateActivePersonaStatus(
|
||||||
userPersonas,
|
userPersonas,
|
||||||
activeUserPersonaId,
|
activeUserPersonaId,
|
||||||
activeUserPersonaStatus,
|
activeUserPersonaStatus,
|
||||||
'No user persona set.',
|
'No user persona set.',
|
||||||
'You are'
|
'User persona active'
|
||||||
);
|
);
|
||||||
clearPersonaBtn.disabled = !activePersonaId;
|
clearPersonaBtn.disabled = !activePersonaId;
|
||||||
clearUserPersonaBtn.disabled = !activeUserPersonaId;
|
clearUserPersonaBtn.disabled = !activeUserPersonaId;
|
||||||
@@ -278,8 +274,15 @@ function renderPersonaList(personaItems, activeId, listElement, personaType) {
|
|||||||
personaItems.forEach(persona => {
|
personaItems.forEach(persona => {
|
||||||
const item = document.createElement('div');
|
const item = document.createElement('div');
|
||||||
item.className = `persona-item${persona.id === activeId ? ' active' : ''}`;
|
item.className = `persona-item${persona.id === activeId ? ' active' : ''}`;
|
||||||
|
const titleRow = document.createElement('div');
|
||||||
|
titleRow.className = 'persona-title-row';
|
||||||
const title = document.createElement('h4');
|
const title = document.createElement('h4');
|
||||||
title.textContent = persona.name;
|
title.textContent = persona.name;
|
||||||
|
const roleTag = document.createElement('span');
|
||||||
|
roleTag.className = `persona-role-tag ${personaType === "user" ? "user" : "ai"}`;
|
||||||
|
roleTag.textContent = personaType === "user" ? "You" : "AI";
|
||||||
|
titleRow.appendChild(title);
|
||||||
|
titleRow.appendChild(roleTag);
|
||||||
const meta = document.createElement('p');
|
const meta = document.createElement('p');
|
||||||
meta.textContent = persona.pronouns ? `Pronouns: ${persona.pronouns}` : 'Pronouns: —';
|
meta.textContent = persona.pronouns ? `Pronouns: ${persona.pronouns}` : 'Pronouns: —';
|
||||||
const actions = document.createElement('div');
|
const actions = document.createElement('div');
|
||||||
@@ -287,7 +290,8 @@ function renderPersonaList(personaItems, activeId, listElement, personaType) {
|
|||||||
|
|
||||||
const equipBtn = document.createElement('button');
|
const equipBtn = document.createElement('button');
|
||||||
equipBtn.type = 'button';
|
equipBtn.type = 'button';
|
||||||
equipBtn.textContent = persona.id === activeId ? 'Equipped' : 'Equip';
|
const equipLabel = personaType === "user" ? "Equip as You" : "Equip as AI";
|
||||||
|
equipBtn.textContent = persona.id === activeId ? `${equipLabel} (Active)` : equipLabel;
|
||||||
equipBtn.disabled = persona.id === activeId;
|
equipBtn.disabled = persona.id === activeId;
|
||||||
equipBtn.addEventListener('click', () => equipPersona(persona.id, personaType));
|
equipBtn.addEventListener('click', () => equipPersona(persona.id, personaType));
|
||||||
|
|
||||||
@@ -312,7 +316,7 @@ function renderPersonaList(personaItems, activeId, listElement, personaType) {
|
|||||||
actions.appendChild(publishBtn);
|
actions.appendChild(publishBtn);
|
||||||
actions.appendChild(deleteBtn);
|
actions.appendChild(deleteBtn);
|
||||||
|
|
||||||
item.appendChild(title);
|
item.appendChild(titleRow);
|
||||||
item.appendChild(meta);
|
item.appendChild(meta);
|
||||||
item.appendChild(actions);
|
item.appendChild(actions);
|
||||||
listElement.appendChild(item);
|
listElement.appendChild(item);
|
||||||
@@ -332,49 +336,6 @@ async function loadPersonas() {
|
|||||||
renderPersonaList(userPersonas, activeUserPersonaId, userPersonaList, "user");
|
renderPersonaList(userPersonas, activeUserPersonaId, userPersonaList, "user");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMarketList(personaItems, listElement) {
|
|
||||||
listElement.innerHTML = '';
|
|
||||||
if (!personaItems.length) {
|
|
||||||
const empty = document.createElement('p');
|
|
||||||
empty.className = 'status persona-status';
|
|
||||||
empty.textContent = 'No market personas yet. Be the first to publish!';
|
|
||||||
listElement.appendChild(empty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
personaItems.forEach(persona => {
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'persona-item';
|
|
||||||
const title = document.createElement('h4');
|
|
||||||
title.textContent = persona.name;
|
|
||||||
const meta = document.createElement('p');
|
|
||||||
const pronouns = persona.pronouns ? `Pronouns: ${persona.pronouns}` : 'Pronouns: —';
|
|
||||||
meta.textContent = `By ${persona.creator_username} • ${pronouns}`;
|
|
||||||
const actions = document.createElement('div');
|
|
||||||
actions.className = 'persona-item-actions';
|
|
||||||
|
|
||||||
const getBtn = document.createElement('button');
|
|
||||||
getBtn.type = 'button';
|
|
||||||
getBtn.textContent = 'Get & equip';
|
|
||||||
getBtn.addEventListener('click', () => collectMarketPersona(persona.id, persona.persona_type));
|
|
||||||
|
|
||||||
actions.appendChild(getBtn);
|
|
||||||
item.appendChild(title);
|
|
||||||
item.appendChild(meta);
|
|
||||||
item.appendChild(actions);
|
|
||||||
listElement.appendChild(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadMarketPersonas() {
|
|
||||||
const res = await get('/personas/market');
|
|
||||||
if (res.error) return;
|
|
||||||
marketPersonas = res.personas || [];
|
|
||||||
const assistantMarket = marketPersonas.filter(persona => persona.persona_type === 'assistant');
|
|
||||||
const userMarket = marketPersonas.filter(persona => persona.persona_type === 'user');
|
|
||||||
renderMarketList(assistantMarket, marketPersonaList);
|
|
||||||
renderMarketList(userMarket, marketUserPersonaList);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function equipPersona(personaId, personaType) {
|
async function equipPersona(personaId, personaType) {
|
||||||
const endpoint = personaType === "user"
|
const endpoint = personaType === "user"
|
||||||
? `/personas/${personaId}/equip-user`
|
? `/personas/${personaId}/equip-user`
|
||||||
@@ -439,18 +400,6 @@ async function publishPersona(personaId) {
|
|||||||
const res = await post(`/personas/${personaId}/publish`, {});
|
const res = await post(`/personas/${personaId}/publish`, {});
|
||||||
if (res.error) return;
|
if (res.error) return;
|
||||||
await loadPersonas();
|
await loadPersonas();
|
||||||
await loadMarketPersonas();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function collectMarketPersona(marketId, personaType) {
|
|
||||||
const res = await post(`/personas/market/${marketId}/collect`, {equip: true});
|
|
||||||
if (res.error) return;
|
|
||||||
if (personaType === "user") {
|
|
||||||
activeUserPersonaId = res.activeUserPersonaId || res.persona?.id || null;
|
|
||||||
} else {
|
|
||||||
activePersonaId = res.activePersonaId || res.persona?.id || null;
|
|
||||||
}
|
|
||||||
await loadPersonas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLoadingState(loading) {
|
function setLoadingState(loading) {
|
||||||
@@ -568,7 +517,6 @@ async function checkSession() {
|
|||||||
chatDiv.style.display = "block";
|
chatDiv.style.display = "block";
|
||||||
await loadChatSessions();
|
await loadChatSessions();
|
||||||
await loadPersonas();
|
await loadPersonas();
|
||||||
await loadMarketPersonas();
|
|
||||||
msgInput.focus();
|
msgInput.focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -578,7 +526,6 @@ async function checkSession() {
|
|||||||
async function loadServerChat() {
|
async function loadServerChat() {
|
||||||
await loadChatSessions();
|
await loadChatSessions();
|
||||||
await loadPersonas();
|
await loadPersonas();
|
||||||
await loadMarketPersonas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleAuth(endpoint, credentials) {
|
async function handleAuth(endpoint, credentials) {
|
||||||
@@ -689,7 +636,6 @@ document.getElementById("logout").onclick = async () => {
|
|||||||
userPersonas = [];
|
userPersonas = [];
|
||||||
activePersonaId = null;
|
activePersonaId = null;
|
||||||
activeUserPersonaId = null;
|
activeUserPersonaId = null;
|
||||||
marketPersonas = [];
|
|
||||||
publishedPersonaIds = new Set();
|
publishedPersonaIds = new Set();
|
||||||
setAuthMessage("Logged out.", 'success');
|
setAuthMessage("Logged out.", 'success');
|
||||||
showAuthScreen('login');
|
showAuthScreen('login');
|
||||||
@@ -733,8 +679,6 @@ clearPersonaBtn.addEventListener('click', () => clearPersona("assistant"));
|
|||||||
|
|
||||||
clearUserPersonaBtn.addEventListener('click', () => clearPersona("user"));
|
clearUserPersonaBtn.addEventListener('click', () => clearPersona("user"));
|
||||||
|
|
||||||
refreshMarketBtn.addEventListener('click', () => loadMarketPersonas());
|
|
||||||
|
|
||||||
personaForm.addEventListener('submit', (event) => {
|
personaForm.addEventListener('submit', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
savePersona();
|
savePersona();
|
||||||
|
|||||||
@@ -324,6 +324,13 @@ button:disabled {
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.secondary-action {
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-list {
|
.chat-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -411,6 +418,36 @@ button:disabled {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.persona-title-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-role-tag {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-role-tag.ai {
|
||||||
|
border-color: rgba(99, 102, 241, 0.6);
|
||||||
|
color: #c7d2fe;
|
||||||
|
background: rgba(99, 102, 241, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.persona-role-tag.user {
|
||||||
|
border-color: rgba(34, 197, 94, 0.6);
|
||||||
|
color: #bbf7d0;
|
||||||
|
background: rgba(34, 197, 94, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
.persona-item.active {
|
.persona-item.active {
|
||||||
border-color: rgba(99, 102, 241, 0.6);
|
border-color: rgba(99, 102, 241, 0.6);
|
||||||
box-shadow: 0 8px 18px rgba(99, 102, 241, 0.18);
|
box-shadow: 0 8px 18px rgba(99, 102, 241, 0.18);
|
||||||
@@ -448,6 +485,13 @@ button:disabled {
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.persona-helper {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
.persona-form.hidden {
|
.persona-form.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -836,6 +880,13 @@ button:disabled {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.persona-guidance {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
.persona-section-actions {
|
.persona-section-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -858,6 +909,42 @@ button:disabled {
|
|||||||
max-height: 220px;
|
max-height: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.market-shell {
|
||||||
|
width: min(980px, 95vw);
|
||||||
|
padding: 28px;
|
||||||
|
display: grid;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.market-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.market-header h1 {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.market-header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.market-page-columns .persona-list {
|
||||||
|
max-height: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.market-link {
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.model-select select:hover, .model-select select:focus {
|
.model-select select:hover, .model-select select:focus {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
background: rgba(15, 15, 35, 1);
|
background: rgba(15, 15, 35, 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user