159 lines
7.0 KiB
JavaScript
159 lines
7.0 KiB
JavaScript
const $ = (id) => document.getElementById(id);
|
|
const marketPersonaList = $("marketPersonaList"), marketUserPersonaList = $("marketUserPersonaList");
|
|
const refreshMarketBtn = $("refreshMarket"), marketStatus = $("marketStatus"), marketSearchInput = $("marketSearch");
|
|
const marketPreviewModal = $("marketPreviewModal"), marketPreviewName = $("marketPreviewName"), marketPreviewMeta = $("marketPreviewMeta");
|
|
const marketPreviewPronouns = $("marketPreviewPronouns"), marketPreviewAppearance = $("marketPreviewAppearance"), marketPreviewBackground = $("marketPreviewBackground"), marketPreviewDetails = $("marketPreviewDetails");
|
|
const marketPreviewConfirm = $("marketPreviewConfirm"), marketPreviewCancel = $("marketPreviewCancel"), marketPreviewClose = $("marketPreviewClose");
|
|
const themeNameTargets = document.querySelectorAll("[data-theme-name]");
|
|
let allMarketPersonas = [], pendingMarketPersona = null;
|
|
|
|
const themes = {
|
|
"fakegpt": {name: "FakeGPT"},
|
|
"fraud": {name: "Fraud"},
|
|
"germini": {name: "Germini"},
|
|
"slopilot": {name: "Slopilot"},
|
|
"beta-ai": {name: "Beta AI"},
|
|
"confusity": {name: "Confusity"}
|
|
};
|
|
|
|
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 {
|
|
return {error: "Network error - please try again."};
|
|
}
|
|
}
|
|
|
|
const get = (url) => request(url, null, "GET");
|
|
const post = (url, data) => request(url, data, "POST");
|
|
|
|
function applyTheme(themeKey) {
|
|
const nextTheme = themes[themeKey] ? themeKey : "fakegpt";
|
|
document.body.dataset.theme = nextTheme;
|
|
document.title = `${themes[nextTheme].name} Market`;
|
|
themeNameTargets.forEach((target) => { target.textContent = themes[nextTheme].name; });
|
|
}
|
|
|
|
function setMarketStatus(message, state = "") {
|
|
marketStatus.textContent = message;
|
|
marketStatus.className = state ? `status persona-status ${state}` : "status persona-status";
|
|
}
|
|
|
|
function buildPersonaMeta(persona) {
|
|
const pronouns = persona.pronouns ? `Pronouns: ${persona.pronouns}` : "Pronouns: n/a";
|
|
return `${persona.creator_username} | ${pronouns}`;
|
|
}
|
|
|
|
function formatPersonaField(value) {
|
|
return value && value.trim() ? value.trim() : "Not provided.";
|
|
}
|
|
|
|
function closeMarketPreview() {
|
|
marketPreviewModal.classList.add("hidden");
|
|
pendingMarketPersona = null;
|
|
}
|
|
|
|
function renderMarketList(items, listElement, type) {
|
|
listElement.innerHTML = "";
|
|
if (!items.length) {
|
|
const empty = document.createElement("p");
|
|
empty.className = "status persona-status";
|
|
empty.textContent = type === "assistant" ? "No AI personas found." : "No user personas found.";
|
|
listElement.appendChild(empty);
|
|
return;
|
|
}
|
|
|
|
items.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");
|
|
const detailsBtn = document.createElement("button");
|
|
|
|
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" : "You";
|
|
title.textContent = persona.name;
|
|
meta.textContent = buildPersonaMeta(persona);
|
|
detailsBtn.type = "button";
|
|
detailsBtn.textContent = "View";
|
|
detailsBtn.addEventListener("click", () => openMarketPreview(persona));
|
|
|
|
titleRow.append(title, tag);
|
|
actions.appendChild(detailsBtn);
|
|
item.append(titleRow, meta, actions);
|
|
listElement.appendChild(item);
|
|
});
|
|
}
|
|
|
|
function filterMarketPersonas() {
|
|
const query = marketSearchInput.value.trim().toLowerCase();
|
|
if (!query) return allMarketPersonas;
|
|
return allMarketPersonas.filter((persona) =>
|
|
[persona.name, persona.creator_username, persona.pronouns, persona.appearance, persona.background, persona.details]
|
|
.some((field) => field && field.toLowerCase().includes(query))
|
|
);
|
|
}
|
|
|
|
function applyMarketFilter() {
|
|
const filtered = filterMarketPersonas();
|
|
renderMarketList(filtered.filter((persona) => persona.persona_type === "assistant"), marketPersonaList, "assistant");
|
|
renderMarketList(filtered.filter((persona) => persona.persona_type === "user"), marketUserPersonaList, "user");
|
|
if (marketSearchInput.value.trim() && !filtered.length) {
|
|
setMarketStatus("No personas match your search.", "error");
|
|
return;
|
|
}
|
|
setMarketStatus(`${filtered.length} persona${filtered.length === 1 ? "" : "s"} shown.`);
|
|
}
|
|
|
|
function openMarketPreview(persona) {
|
|
pendingMarketPersona = persona;
|
|
marketPreviewName.textContent = persona.name;
|
|
marketPreviewMeta.textContent = buildPersonaMeta(persona);
|
|
marketPreviewPronouns.textContent = formatPersonaField(persona.pronouns);
|
|
marketPreviewAppearance.textContent = formatPersonaField(persona.appearance);
|
|
marketPreviewBackground.textContent = formatPersonaField(persona.background);
|
|
marketPreviewDetails.textContent = formatPersonaField(persona.details);
|
|
marketPreviewConfirm.textContent = persona.persona_type === "assistant" ? "Collect as AI persona" : "Collect as user persona";
|
|
marketPreviewModal.classList.remove("hidden");
|
|
}
|
|
|
|
async function loadMarketPersonas() {
|
|
setMarketStatus("Loading market personas...");
|
|
const res = await get("/personas/market");
|
|
if (res.error) return setMarketStatus(res.error, "error");
|
|
allMarketPersonas = res.personas || [];
|
|
applyMarketFilter();
|
|
}
|
|
|
|
async function collectMarketPersona(marketId, personaType) {
|
|
setMarketStatus(personaType === "assistant" ? "Collecting AI persona..." : "Collecting user persona...");
|
|
const res = await post(`/personas/market/${marketId}/collect`, {equip: true});
|
|
if (res.error) return setMarketStatus(res.error, "error");
|
|
await loadMarketPersonas();
|
|
setMarketStatus(personaType === "assistant" ? "AI persona collected." : "User persona collected.", "success");
|
|
}
|
|
|
|
refreshMarketBtn.addEventListener("click", () => { void loadMarketPersonas(); });
|
|
marketSearchInput.addEventListener("input", applyMarketFilter);
|
|
marketPreviewCancel.addEventListener("click", closeMarketPreview);
|
|
marketPreviewClose.addEventListener("click", closeMarketPreview);
|
|
marketPreviewModal.addEventListener("click", (event) => { if (event.target === marketPreviewModal) closeMarketPreview(); });
|
|
marketPreviewConfirm.addEventListener("click", async () => {
|
|
if (!pendingMarketPersona) return;
|
|
const marketId = pendingMarketPersona.id;
|
|
const personaType = pendingMarketPersona.persona_type;
|
|
closeMarketPreview();
|
|
await collectMarketPersona(marketId, personaType);
|
|
});
|
|
|
|
window.addEventListener("load", () => {
|
|
applyTheme(localStorage.getItem("krishd-theme") || "fakegpt");
|
|
void loadMarketPersonas();
|
|
});
|