c.AI Search Sort

Sort search so cards with public definition stays on top and marked with a star

当前为 2024-10-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name c.AI Search Sort
  3. // @author EnergoStalin
  4. // @description Sort search so cards with public definition stays on top and marked with a star
  5. // @license AGPL-3.0-only
  6. // @version 1.0.2
  7. // @namespace https://c.ai
  8. // @match https://character.ai/search*
  9. // @run-at document-body
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=character.ai
  11. // ==/UserScript==
  12.  
  13. (async () => {
  14. var __defProp = Object.defineProperty;
  15. var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
  16.  
  17. // src/util.ts
  18. async function waitNotNull(func, timeout = 1e4, interval = 1e3) {
  19. return new Promise((res, rej) => {
  20. let time = timeout;
  21. const i = setInterval(async () => {
  22. const c = await func();
  23. time -= interval;
  24. if (time <= 0) {
  25. clearInterval(i);
  26. rej();
  27. }
  28. if (!c) return;
  29. clearInterval(i);
  30. res(c);
  31. }, interval);
  32. });
  33. }
  34. __name(waitNotNull, "waitNotNull");
  35.  
  36. // src/api.ts
  37. var pageProps = await waitNotNull(() => document.querySelector("#__NEXT_DATA__")?.textContent).then((e) => JSON.parse(e).props.pageProps);
  38. var token = pageProps.token;
  39. async function getCharacterInfo(id) {
  40. return await fetch(`https://plus.character.ai/chat/character/info/`, {
  41. headers: {
  42. Authorization: `Token ${token}`,
  43. Origin: "https://character.ai/",
  44. Referer: "https://character.ai/",
  45. "Content-Type": "application/json",
  46. Accept: "application/json"
  47. },
  48. method: "POST",
  49. body: JSON.stringify({
  50. external_id: id
  51. })
  52. }).then((e) => e.json()).then((e) => e.character);
  53. }
  54. __name(getCharacterInfo, "getCharacterInfo");
  55.  
  56. // src/icons.ts
  57. var starredIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#75FB4C"><path d="M371.01-324 480-390.22 589-324l-29-124 97-84-127-11-50-117-50 117-127 11 96.89 83.95L371.01-324ZM480-72 360-192H192v-168L72-480l120-120v-168h168l120-120 120 120h168v168l120 120-120 120v168H600L480-72Zm0-102 90-90h126v-126l90-90-90-90v-126H570l-90-90-90 90H264v126l-90 90 90 90v126h126l90 90Zm0-306Z"/></svg>';
  58. var pendingIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="#5985E1"><path d="M288-420q25 0 42.5-17.5T348-480q0-25-17.5-42.5T288-540q-25 0-42.5 17.5T228-480q0 25 17.5 42.5T288-420Zm192 0q25 0 42.5-17.5T540-480q0-25-17.5-42.5T480-540q-25 0-42.5 17.5T420-480q0 25 17.5 42.5T480-420Zm192 0q25 0 42.5-17.5T732-480q0-25-17.5-42.5T672-540q-25 0-42.5 17.5T612-480q0 25 17.5 42.5T672-420ZM480.28-96Q401-96 331-126t-122.5-82.5Q156-261 126-330.96t-30-149.5Q96-560 126-629.5q30-69.5 82.5-122T330.96-834q69.96-30 149.5-30t149.04 30q69.5 30 122 82.5T834-629.28q30 69.73 30 149Q864-401 834-331t-82.5 122.5Q699-156 629.28-126q-69.73 30-149 30Zm-.28-72q130 0 221-91t91-221q0-130-91-221t-221-91q-130 0-221 91t-91 221q0 130 91 221t221 91Zm0-312Z"/></svg>';
  59.  
  60. // src/statuses.ts
  61. function clearStatus(card) {
  62. card.querySelector("div[data-status]")?.remove();
  63. }
  64. __name(clearStatus, "clearStatus");
  65. function statusWrapper(card, status) {
  66. const height = card.querySelector("img")?.height;
  67. const d = document.createElement("div");
  68. d.dataset.status = status;
  69. d.classList.add("flex", "items-center", "relative", "flex-row");
  70. d.style = `min-height: ${height}px; flex-direction: column;`;
  71. card.append(d);
  72. return d;
  73. }
  74. __name(statusWrapper, "statusWrapper");
  75. function isStarred(card) {
  76. return Boolean(card.querySelector('div[data-status="starred"]'));
  77. }
  78. __name(isStarred, "isStarred");
  79. function setStarredStatus(card, label) {
  80. statusWrapper(card, "starred").innerHTML = `
  81. <div class="flex w-full" style="justify-content: center;">
  82. ${starredIcon}
  83. </div>
  84. <span class="w-full">${label}</span>
  85. `;
  86. }
  87. __name(setStarredStatus, "setStarredStatus");
  88. function setPendingStatus(card) {
  89. statusWrapper(card, "pending").innerHTML = `
  90. <div class="flex w-full items-center">
  91. ${pendingIcon}
  92. </div>
  93. `;
  94. }
  95. __name(setPendingStatus, "setPendingStatus");
  96.  
  97. // src/sorting/definition.ts
  98. async function _sort(container) {
  99. const nodes = Array.from(container.childNodes);
  100. const promises = nodes.map(async (card) => {
  101. if (isStarred(card)) return [];
  102. setPendingStatus(card);
  103. const info = await getCharacterInfo(card.href.split("/").pop());
  104. clearStatus(card);
  105. if (info.definition) {
  106. setStarredStatus(card, `${info.description.length}/${info.definition.length}`);
  107. } else {
  108. container.append(card);
  109. }
  110. return [
  111. card,
  112. info.definition?.length,
  113. info.description?.length
  114. ];
  115. });
  116. return Promise.all(promises);
  117. }
  118. __name(_sort, "_sort");
  119. function sortByDefinitionLength(entries, container) {
  120. 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));
  121. }
  122. __name(sortByDefinitionLength, "sortByDefinitionLength");
  123. async function sort(observer, container) {
  124. observer.disconnect();
  125. sortByDefinitionLength(await _sort(container), container);
  126. observer.observe(container, {
  127. attributes: false,
  128. childList: true,
  129. subtree: false
  130. });
  131. }
  132. __name(sort, "sort");
  133.  
  134. // src/index.ts
  135. var cardsContainer = await waitNotNull(() => document.evaluate("/html/body/div[1]/div/main/div/div/div/main/div/div[2]", document).iterateNext());
  136. var sortSearches = /* @__PURE__ */ __name((_, observer) => sort(observer, cardsContainer), "sortSearches");
  137. sortSearches([], new MutationObserver(sortSearches));
  138. })()