Civitai Multiple Prompt Copy Buttons (with Prompt Management)

Adds multiple prompt copy buttons in a compact, draggable floating menu with prompt management on Civitai's generate page

  1. // ==UserScript==
  2. // @name Civitai Multiple Prompt Copy Buttons (with Prompt Management)
  3. // @namespace http://tampermonkey.net/
  4. // @version 4.0
  5. // @description Adds multiple prompt copy buttons in a compact, draggable floating menu with prompt management on Civitai's generate page
  6. // @match https://civitai.com/generate
  7. // @grant GM_setClipboard
  8. // @grant GM_addStyle
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @license MIT
  12.  
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. let prompts = GM_getValue("savedPrompts", {
  19. 'Ryuko': "matoi ryuuko, kill la kill, 1girl, black hair, blue eyes, medium hair, multicolored hair, red hair, streaked hair, two-tone hair",
  20. 'Low angle': "from below, low angle, ground level",
  21. 'High angle': "from above, high angle",
  22. 'Kuroneko': "in the style of Kanzaki Hiro, 1girl, gokou ruri, black hair, blush, hairclip, hairband, hime cut, purple eyes, hair flower, hair ornament, hairclip, red eyes",
  23. 'Toki': "toki (blue archive), blue archive, 1girl, blonde hair, blue eyes, blue hairband, blue halo, hairband, halo, solo",
  24. 'Oily': "wet, wet skin, shiny, shiny skin, glossy, glossy skin, oil, oily, oily skin, sweat, sweaty, sweaty skin",
  25. 'Dark': "dark-skinned female, dark skin, tan, tanline",
  26. 'Blonde': "blonde hair, hime cut, long hair, straight hair, hairband"
  27. });
  28.  
  29. const colors = ['#8B0000', '#00008B', '#8B008B', '#006400', '#008B8B', '#8B4500', '#8B8B00'];
  30.  
  31. GM_addStyle(`
  32. #prompt-buttons-container {
  33. position: fixed;
  34. z-index: 9999;
  35. background-color: rgba(26,26,26,0.9);
  36. border-radius: 8px;
  37. box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  38. font-family: Arial, sans-serif;
  39. width: 240px;
  40. display: flex;
  41. flex-direction: column;
  42. }
  43. #drag-handle {
  44. height: 24px;
  45. background-color: #2a2a2a;
  46. border-top-left-radius: 8px;
  47. border-top-right-radius: 8px;
  48. cursor: move;
  49. display: flex;
  50. justify-content: space-between;
  51. align-items: center;
  52. padding: 0 8px;
  53. }
  54. #buttons-grid {
  55. display: grid;
  56. grid-template-columns: repeat(3, 1fr);
  57. gap: 4px;
  58. padding: 4px;
  59. max-height: 300px;
  60. overflow-y: auto;
  61. }
  62. .prompt-button, #show-hide-btn, #manage-btn {
  63. padding: 6px;
  64. color: white;
  65. border: none;
  66. cursor: pointer;
  67. font-size: 11px;
  68. font-weight: bold;
  69. transition: all 0.2s;
  70. text-align: center;
  71. border-radius: 4px;
  72. }
  73. .prompt-button:hover, #show-hide-btn:hover, #manage-btn:hover {
  74. filter: brightness(1.2);
  75. transform: translateY(-1px);
  76. }
  77. #show-hide-btn, #manage-btn {
  78. background-color: #4a4a4a;
  79. width: 50px;
  80. }
  81. #pin-btn {
  82. cursor: pointer;
  83. color: #fff;
  84. font-size: 14px;
  85. background: none;
  86. border: none;
  87. padding: 0;
  88. }
  89. #manage-panel {
  90. display: none;
  91. padding: 8px;
  92. background-color: #2a2a2a;
  93. }
  94. #manage-panel input, #manage-panel textarea {
  95. width: 100%;
  96. margin-bottom: 4px;
  97. padding: 4px;
  98. }
  99. #manage-panel button {
  100. margin-right: 4px;
  101. margin-bottom: 4px;
  102. }
  103. `);
  104.  
  105. function createButton(name, prompt, color) {
  106. const button = document.createElement('button');
  107. button.className = 'prompt-button';
  108. button.textContent = name;
  109. button.style.backgroundColor = color;
  110. button.onclick = (e) => {
  111. e.stopPropagation();
  112. GM_setClipboard(prompt);
  113. const originalText = button.textContent;
  114. button.textContent = 'Copied!';
  115. setTimeout(() => button.textContent = originalText, 1000);
  116. };
  117. return button;
  118. }
  119.  
  120. function makeDraggable(container, handle) {
  121. let isDragging = false;
  122. let startX, startY, startLeft, startTop;
  123.  
  124. handle.addEventListener('mousedown', startDragging);
  125. document.addEventListener('mousemove', drag);
  126. document.addEventListener('mouseup', stopDragging);
  127.  
  128. function startDragging(e) {
  129. isDragging = true;
  130. startX = e.clientX;
  131. startY = e.clientY;
  132. startLeft = parseInt(container.style.left) || 0;
  133. startTop = parseInt(container.style.top) || 0;
  134. e.preventDefault();
  135. }
  136.  
  137. function drag(e) {
  138. if (!isDragging) return;
  139. if (container.dataset.pinned === 'true') return;
  140. const deltaX = e.clientX - startX;
  141. const deltaY = e.clientY - startY;
  142. container.style.left = `${startLeft + deltaX}px`;
  143. container.style.top = `${startTop + deltaY}px`;
  144. savePosition(startLeft + deltaX, startTop + deltaY);
  145. }
  146.  
  147. function stopDragging() {
  148. isDragging = false;
  149. }
  150. }
  151.  
  152. function savePosition(x, y) {
  153. GM_setValue("containerX", x);
  154. GM_setValue("containerY", y);
  155. }
  156.  
  157. function loadPosition(container) {
  158. const x = GM_getValue("containerX", window.innerWidth - 260);
  159. const y = GM_getValue("containerY", window.innerHeight - 300);
  160. container.style.left = `${x}px`;
  161. container.style.top = `${y}px`;
  162. }
  163.  
  164. function updateButtonsGrid() {
  165. const buttonGrid = document.getElementById('buttons-grid');
  166. buttonGrid.innerHTML = '';
  167. Object.entries(prompts).forEach(([name, prompt], index) => {
  168. const button = createButton(name, prompt, colors[index % colors.length]);
  169. buttonGrid.appendChild(button);
  170. });
  171. GM_setValue("savedPrompts", prompts);
  172. }
  173.  
  174. function addPromptButtons() {
  175. const container = document.createElement('div');
  176. container.id = 'prompt-buttons-container';
  177.  
  178. const dragHandle = document.createElement('div');
  179. dragHandle.id = 'drag-handle';
  180.  
  181. const showHideBtn = document.createElement('button');
  182. showHideBtn.id = 'show-hide-btn';
  183. showHideBtn.textContent = 'Hide';
  184. showHideBtn.onclick = toggleButtons;
  185.  
  186. const manageBtn = document.createElement('button');
  187. manageBtn.id = 'manage-btn';
  188. manageBtn.textContent = 'Manage';
  189. manageBtn.onclick = toggleManagePanel;
  190.  
  191. const pinBtn = document.createElement('button');
  192. pinBtn.id = 'pin-btn';
  193. pinBtn.innerHTML = '📌';
  194. pinBtn.onclick = togglePin;
  195.  
  196. dragHandle.appendChild(showHideBtn);
  197. dragHandle.appendChild(manageBtn);
  198. dragHandle.appendChild(pinBtn);
  199.  
  200. const buttonGrid = document.createElement('div');
  201. buttonGrid.id = 'buttons-grid';
  202.  
  203. const managePanel = createManagePanel();
  204.  
  205. container.appendChild(dragHandle);
  206. container.appendChild(buttonGrid);
  207. container.appendChild(managePanel);
  208. document.body.appendChild(container);
  209.  
  210. updateButtonsGrid();
  211. makeDraggable(container, dragHandle);
  212. loadPosition(container);
  213. }
  214.  
  215. function toggleButtons() {
  216. const buttonGrid = document.getElementById('buttons-grid');
  217. const showHideBtn = document.getElementById('show-hide-btn');
  218. if (buttonGrid.style.display === 'none') {
  219. buttonGrid.style.display = 'grid';
  220. showHideBtn.textContent = 'Hide';
  221. } else {
  222. buttonGrid.style.display = 'none';
  223. showHideBtn.textContent = 'Show';
  224. }
  225. }
  226.  
  227. function togglePin() {
  228. const container = document.getElementById('prompt-buttons-container');
  229. const pinBtn = document.getElementById('pin-btn');
  230. if (container.dataset.pinned === 'true') {
  231. delete container.dataset.pinned;
  232. pinBtn.style.opacity = '1';
  233. } else {
  234. container.dataset.pinned = 'true';
  235. pinBtn.style.opacity = '0.5';
  236. }
  237. }
  238.  
  239. function toggleManagePanel() {
  240. const managePanel = document.getElementById('manage-panel');
  241. managePanel.style.display = managePanel.style.display === 'none' ? 'block' : 'none';
  242. }
  243.  
  244. function createManagePanel() {
  245. const panel = document.createElement('div');
  246. panel.id = 'manage-panel';
  247. panel.style.display = 'none';
  248.  
  249. const nameInput = document.createElement('input');
  250. nameInput.placeholder = 'Prompt Name';
  251.  
  252. const promptInput = document.createElement('textarea');
  253. promptInput.placeholder = 'Prompt Text';
  254.  
  255. const addButton = document.createElement('button');
  256. addButton.textContent = 'Add';
  257. addButton.onclick = () => {
  258. if (nameInput.value && promptInput.value) {
  259. prompts[nameInput.value] = promptInput.value;
  260. updateButtonsGrid();
  261. nameInput.value = '';
  262. promptInput.value = '';
  263. }
  264. };
  265.  
  266. const editButton = document.createElement('button');
  267. editButton.textContent = 'Edit';
  268. editButton.onclick = () => {
  269. if (nameInput.value && promptInput.value && prompts.hasOwnProperty(nameInput.value)) {
  270. prompts[nameInput.value] = promptInput.value;
  271. updateButtonsGrid();
  272. }
  273. };
  274.  
  275. const removeButton = document.createElement('button');
  276. removeButton.textContent = 'Remove';
  277. removeButton.onclick = () => {
  278. if (prompts.hasOwnProperty(nameInput.value)) {
  279. delete prompts[nameInput.value];
  280. updateButtonsGrid();
  281. nameInput.value = '';
  282. promptInput.value = '';
  283. }
  284. };
  285.  
  286. panel.appendChild(nameInput);
  287. panel.appendChild(promptInput);
  288. panel.appendChild(addButton);
  289. panel.appendChild(editButton);
  290. panel.appendChild(removeButton);
  291.  
  292. return panel;
  293. }
  294.  
  295. if (document.readyState === 'loading') {
  296. document.addEventListener('DOMContentLoaded', addPromptButtons);
  297. } else {
  298. addPromptButtons();
  299. }
  300. })();