// ==UserScript== // @name c.AI Search Sort // @author EnergoStalin // @description Sort search so cards with public definition stays on top and marked with a star // @license AGPL-3.0-only // @version 1.0.3 // @namespace https://c.ai // @match https://character.ai/search* // @run-at document-body // @icon https://www.google.com/s2/favicons?sz=64&domain=character.ai // @grant GM.addStyle // @downloadURL none // ==/UserScript== (async () => { var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/util.ts async function waitNotNull(func, timeout = 1e4, interval = 1e3) { return new Promise((res, rej) => { let time = timeout; const i = setInterval(async () => { const c = await func(); time -= interval; if (time <= 0) { clearInterval(i); rej(); } if (!c) return; clearInterval(i); res(c); }, interval); }); } __name(waitNotNull, "waitNotNull"); // src/api.ts var pageProps = await waitNotNull(() => document.querySelector("#__NEXT_DATA__")?.textContent).then((e) => JSON.parse(e).props.pageProps); var token = pageProps.token; async function getCharacterInfo(id) { return await fetch(`https://plus.character.ai/chat/character/info/`, { headers: { Authorization: `Token ${token}`, Origin: "https://character.ai/", Referer: "https://character.ai/", "Content-Type": "application/json", Accept: "application/json" }, method: "POST", body: JSON.stringify({ external_id: id }) }).then((e) => e.json()).then((e) => e.character); } __name(getCharacterInfo, "getCharacterInfo"); // src/styles/tooltip.css GM.addStyle(` .tooltip { position: relative; cursor: pointer; } .tooltip .tooltiptext { visibility: hidden; text-align: left; z-index: 1; opacity: 0; transition: opacity 0.3s; font-size: 0.7em; color: #626262; } .tooltip:hover .tooltiptext { visibility: visible; opacity: 1; } `); // src/icons.ts var starredIcon = ''; var pendingIcon = ''; // src/statuses.ts function clearStatus(card) { card.querySelector("div[data-status]")?.remove(); } __name(clearStatus, "clearStatus"); function statusWrapper(card, status) { const height = card.querySelector("img")?.height; const d = document.createElement("div"); d.dataset.status = status; d.classList.add("flex", "items-center", "relative", "flex-row", "tooltip"); d.style = `min-height: ${height}px; flex-direction: column;`; card.append(d); return d; } __name(statusWrapper, "statusWrapper"); function isStarred(card) { return Boolean(card.querySelector('div[data-status="starred"]')); } __name(isStarred, "isStarred"); function setStarredStatus(card, label) { statusWrapper(card, "starred").innerHTML = `
${starredIcon}
${label} `; } __name(setStarredStatus, "setStarredStatus"); function setPendingStatus(card) { statusWrapper(card, "pending").innerHTML = `
${pendingIcon}
`; } __name(setPendingStatus, "setPendingStatus"); // src/sorting/definition.ts async function _sort(container) { const nodes = Array.from(container.childNodes); const promises = nodes.map(async (card) => { if (isStarred(card)) return []; setPendingStatus(card); const info = await getCharacterInfo(card.href.split("/").pop()); clearStatus(card); if (info.definition) { setStarredStatus(card, `${info.description.length}/${info.definition.length}`); } else { container.append(card); } return [ card, info.definition?.length, info.description?.length ]; }); return Promise.all(promises); } __name(_sort, "_sort"); function sortByDefinitionLength(entries, container) { entries.filter(([_c, dl, _dl]) => (dl ?? 0) !== 0).sort(([_c1, dl1, _dl1], [_c2, dl2, _dl2]) => dl1 < dl2 ? 1 : -1).map(([c, _dfl, _dsl]) => c).reverse().forEach((e) => container.insertBefore(e, container.firstChild)); } __name(sortByDefinitionLength, "sortByDefinitionLength"); async function sort(observer, container) { observer.disconnect(); sortByDefinitionLength(await _sort(container), container); observer.observe(container, { attributes: false, childList: true, subtree: false }); } __name(sort, "sort"); // src/index.ts var cardsContainer = await waitNotNull(() => document.evaluate("/html/body/div[1]/div/main/div/div/div/main/div/div[2]", document).iterateNext()); var sortSearches = /* @__PURE__ */ __name((_, observer) => sort(observer, cardsContainer), "sortSearches"); sortSearches([], new MutationObserver(sortSearches)); })()