PixAI UI Helpers

Helpers for PixAI: Toggle between 4x1 and 2x2 layouts for the image grid, toggle the visibility of the right and left sidebars, and enable "Ctrl+Enter" for generating images. Remembers layout and Ctrl+Enter settings across refreshes.

  1. // ==UserScript==
  2. // @name PixAI UI Helpers
  3. // @namespace http://yourname.tampermonkey.net/
  4. // @version 1.4.2
  5. // @description Helpers for PixAI: Toggle between 4x1 and 2x2 layouts for the image grid, toggle the visibility of the right and left sidebars, and enable "Ctrl+Enter" for generating images. Remembers layout and Ctrl+Enter settings across refreshes.
  6. // @author Yada
  7. // @match https://pixai.art/*
  8. // @icon https://pixai.art/favicon.ico
  9. // @grant none
  10. // @license MIT
  11. // @supportURL http://yoursupporturl.com/script.js
  12. // ==/UserScript==
  13. (function() {
  14. 'use strict';
  15.  
  16. const LAYOUT_MODES = {
  17. FOUR_BY_ONE: '4x1',
  18. TWO_BY_TWO: '2x2',
  19. };
  20.  
  21. let layoutMode = localStorage.getItem('pixai-layout') || LAYOUT_MODES.FOUR_BY_ONE;
  22. let isCtrlEnterEnabled = localStorage.getItem('pixai-ctrl-enter') === 'enabled';
  23. let isRightBarCollapsed = false;
  24. let isLeftBarCollapsed = false;
  25. let buttonsAdded = false;
  26. let layoutInterval;
  27.  
  28. // Function to get the target element for grid layout
  29. function getGridTargetElement() {
  30. return document.querySelector('#workbench-layout main > div > div:nth-child(2) > div > div:nth-child(1)');
  31. }
  32.  
  33. // Function to set the layout to 4x1
  34. function setLayout4x1() {
  35. const imageContainer = getGridTargetElement();
  36. if (imageContainer) {
  37. imageContainer.style.setProperty('--grid-cols', '4');
  38. imageContainer.style.setProperty('--grid-rows', '1');
  39. layoutMode = LAYOUT_MODES.FOUR_BY_ONE;
  40. localStorage.setItem('pixai-layout', LAYOUT_MODES.FOUR_BY_ONE);
  41. }
  42. }
  43.  
  44. // Function to set the layout to 2x2
  45. function setLayout2x2() {
  46. const imageContainer = getGridTargetElement();
  47. if (imageContainer) {
  48. imageContainer.style.setProperty('--grid-cols', '2');
  49. imageContainer.style.setProperty('--grid-rows', '2');
  50. layoutMode = LAYOUT_MODES.TWO_BY_TWO;
  51. localStorage.setItem('pixai-layout', LAYOUT_MODES.TWO_BY_TWO);
  52. }
  53. }
  54.  
  55. // Function to reapply the current layout
  56. function reapplyLayout() {
  57. switch (layoutMode) {
  58. case LAYOUT_MODES.FOUR_BY_ONE:
  59. setLayout4x1();
  60. break;
  61. case LAYOUT_MODES.TWO_BY_TWO:
  62. setLayout2x2();
  63. break;
  64. }
  65. }
  66.  
  67. // Function to toggle between layouts
  68. function toggleLayout() {
  69. if (layoutMode === LAYOUT_MODES.FOUR_BY_ONE) {
  70. setLayout2x2();
  71. } else {
  72. setLayout4x1();
  73. }
  74. updateLayoutButtonText();
  75. }
  76.  
  77. // Function to update the layout button text to reflect the current mode
  78. function updateLayoutButtonText() {
  79. const toggleLayoutButton = document.querySelector('#pixai-toggle-layout-button');
  80. if (toggleLayoutButton) {
  81. toggleLayoutButton.textContent = `Layout: ${layoutMode.toUpperCase()}`;
  82. }
  83. }
  84.  
  85. // Function to set right bar width to zero
  86. function setRightBarWidthToZero() {
  87. const workbenchLayout = document.querySelector('#workbench-layout');
  88. if (workbenchLayout) {
  89. workbenchLayout.style.gridTemplateColumns = 'min-content 1fr 0px';
  90. isRightBarCollapsed = true;
  91. }
  92. }
  93.  
  94. // Function to restore the original width of the right bar
  95. function restoreRightBarWidth() {
  96. const workbenchLayout = document.querySelector('#workbench-layout');
  97. if (workbenchLayout) {
  98. workbenchLayout.style.gridTemplateColumns = 'min-content 1fr 380px';
  99. isRightBarCollapsed = false;
  100. }
  101. }
  102.  
  103. // Function to toggle the right bar's visibility
  104. function toggleRightBar() {
  105. if (isRightBarCollapsed) {
  106. restoreRightBarWidth();
  107. } else {
  108. setRightBarWidthToZero();
  109. }
  110. }
  111.  
  112. // Function to set left bar width to zero
  113. function setLeftBarWidthToZero() {
  114. const leftBar = document.querySelector('#workbench-layout > div:nth-child(2) > div');
  115. if (leftBar) {
  116. leftBar.style.width = '0px';
  117. leftBar.style.minWidth = '0px';
  118. isLeftBarCollapsed = true;
  119. }
  120. }
  121.  
  122. // Function to restore the original width of the left bar
  123. function restoreLeftBarWidth() {
  124. const leftBar = document.querySelector('#workbench-layout > div:nth-child(2) > div');
  125. if (leftBar) {
  126. leftBar.style.width = '7rem';
  127. leftBar.style.minWidth = '7rem';
  128. isLeftBarCollapsed = false;
  129. }
  130. }
  131.  
  132. // Function to toggle the left bar's visibility
  133. function toggleLeftBar() {
  134. if (isLeftBarCollapsed) {
  135. restoreLeftBarWidth();
  136. } else {
  137. setLeftBarWidthToZero();
  138. }
  139. }
  140.  
  141. // Function to handle Ctrl+Enter key press for generating images
  142. function handleCtrlEnter(event) {
  143. if (event.ctrlKey && event.key === 'Enter') {
  144. const generateButton = document.querySelector('[data-tutorial-target="generate-button"]');
  145. if (generateButton) {
  146. generateButton.click();
  147. }
  148. }
  149. }
  150.  
  151. // Function to enable Ctrl+Enter for generating images
  152. function enableCtrlEnter() {
  153. if (!isCtrlEnterEnabled) {
  154. document.addEventListener('keydown', handleCtrlEnter);
  155. isCtrlEnterEnabled = true;
  156. localStorage.setItem('pixai-ctrl-enter', 'enabled');
  157. }
  158. }
  159.  
  160. // Function to disable Ctrl+Enter for generating images
  161. function disableCtrlEnter() {
  162. if (isCtrlEnterEnabled) {
  163. document.removeEventListener('keydown', handleCtrlEnter);
  164. isCtrlEnterEnabled = false;
  165. localStorage.setItem('pixai-ctrl-enter', 'disabled');
  166. }
  167. }
  168.  
  169. // Function to toggle Ctrl+Enter feature
  170. function toggleCtrlEnter() {
  171. if (isCtrlEnterEnabled) {
  172. disableCtrlEnter();
  173. } else {
  174. enableCtrlEnter();
  175. }
  176. }
  177.  
  178. // Function to add the buttons
  179. function addButtons() {
  180. if (buttonsAdded) return;
  181.  
  182. // Create button container for right side
  183. const buttonContainer = document.createElement('div');
  184. buttonContainer.id = 'pixai-ui-helpers-buttons';
  185. buttonContainer.style.position = 'fixed';
  186. buttonContainer.style.top = '0';
  187. buttonContainer.style.right = '0';
  188. buttonContainer.style.zIndex = '1000';
  189. buttonContainer.style.display = 'flex';
  190. buttonContainer.style.flexDirection = 'row';
  191. buttonContainer.style.gap = '5px';
  192. buttonContainer.style.paddingTop = '10px';
  193.  
  194. // Button styles
  195. const buttonStyle = {
  196. display: 'flex',
  197. justifyContent: 'center',
  198. alignItems: 'center',
  199. backgroundColor: 'transparent',
  200. color: 'white',
  201. fontFamily: 'Inter, sans-serif',
  202. fontSize: '0.75rem',
  203. fontWeight: '500',
  204. textShadow: '0 1px 2px rgba(0, 0, 0, 0.5)',
  205. padding: '0.5rem 1rem',
  206. border: 'none',
  207. borderRadius: '0.25rem',
  208. cursor: 'pointer',
  209. transition: 'background-color 0.3s',
  210. };
  211.  
  212. // Create toggle layout button
  213. const toggleLayoutButton = document.createElement('button');
  214. toggleLayoutButton.id = 'pixai-toggle-layout-button';
  215. toggleLayoutButton.textContent = `Layout: ${layoutMode.toUpperCase()}`;
  216. Object.assign(toggleLayoutButton.style, buttonStyle);
  217. toggleLayoutButton.onclick = toggleLayout;
  218.  
  219. // Create toggle right bar button
  220. const toggleRightBarButton = document.createElement('button');
  221. toggleRightBarButton.textContent = 'Toggle Right Bar';
  222. Object.assign(toggleRightBarButton.style, buttonStyle);
  223. toggleRightBarButton.onclick = toggleRightBar;
  224.  
  225. // Create toggle Ctrl+Enter button
  226. const toggleCtrlEnterButton = document.createElement('button');
  227. toggleCtrlEnterButton.textContent = isCtrlEnterEnabled ? 'Disable Ctrl+Enter' : 'Enable Ctrl+Enter';
  228. Object.assign(toggleCtrlEnterButton.style, buttonStyle);
  229. toggleCtrlEnterButton.onclick = function() {
  230. toggleCtrlEnter();
  231. toggleCtrlEnterButton.textContent = isCtrlEnterEnabled ? 'Disable Ctrl+Enter' : 'Enable Ctrl+Enter';
  232. };
  233.  
  234. // Append buttons to right container
  235. buttonContainer.appendChild(toggleCtrlEnterButton);
  236. buttonContainer.appendChild(toggleLayoutButton);
  237. buttonContainer.appendChild(toggleRightBarButton);
  238.  
  239.  
  240. // Append button container to body
  241. document.body.appendChild(buttonContainer);
  242.  
  243. // Extend the clickable area of the right buttons
  244. buttonContainer.style.paddingTop = '0';
  245. buttonContainer.style.height = '40px';
  246. buttonContainer.style.alignItems = 'center';
  247.  
  248. // Create button container for left side
  249. const leftButtonContainer = document.createElement('div');
  250. leftButtonContainer.id = 'pixai-ui-helpers-left-buttons';
  251. leftButtonContainer.style.position = 'fixed';
  252. leftButtonContainer.style.top = '0';
  253. leftButtonContainer.style.left = '0';
  254. leftButtonContainer.style.zIndex = '1000';
  255. leftButtonContainer.style.display = 'flex';
  256. leftButtonContainer.style.flexDirection = 'column';
  257. leftButtonContainer.style.paddingTop = '10px';
  258.  
  259. // Create toggle left bar button
  260. const toggleLeftBarButton = document.createElement('button');
  261. toggleLeftBarButton.textContent = 'Toggle Left Bar';
  262. Object.assign(toggleLeftBarButton.style, buttonStyle);
  263. toggleLeftBarButton.onclick = toggleLeftBar;
  264.  
  265. // Append button to left container
  266. leftButtonContainer.appendChild(toggleLeftBarButton);
  267.  
  268. // Extend the clickable area of the left button
  269. leftButtonContainer.style.paddingTop = '0';
  270. leftButtonContainer.style.height = '40px';
  271. leftButtonContainer.style.alignItems = 'center';
  272.  
  273. // Append left button container to body
  274. document.body.appendChild(leftButtonContainer);
  275.  
  276. buttonsAdded = true;
  277.  
  278. // Start interval to reapply layout every 0.1 seconds
  279. layoutInterval = setInterval(reapplyLayout, 100);
  280.  
  281. // Apply initial settings based on saved preferences
  282. reapplyLayout();
  283. if (isCtrlEnterEnabled) {
  284. enableCtrlEnter();
  285. }
  286. }
  287.  
  288. // Function to remove the buttons and clear the interval
  289. function removeButtons() {
  290. const buttonContainer = document.querySelector('#pixai-ui-helpers-buttons');
  291. const leftButtonContainer = document.querySelector('#pixai-ui-helpers-left-buttons');
  292. if (buttonContainer) {
  293. buttonContainer.remove();
  294. buttonsAdded = false;
  295. }
  296. if (leftButtonContainer) {
  297. leftButtonContainer.remove();
  298. buttonsAdded = false;
  299. }
  300.  
  301. if (layoutInterval) {
  302. clearInterval(layoutInterval);
  303. layoutInterval = null;
  304. }
  305. }
  306.  
  307. // Function to monitor URL changes and add/remove buttons accordingly
  308. function monitorURLChanges() {
  309. const currentURL = window.location.href;
  310.  
  311. if (currentURL.includes('/generator/')) {
  312. addButtons();
  313. } else {
  314. removeButtons();
  315. disableCtrlEnter(); // Ensure Ctrl+Enter is disabled when leaving the page
  316. }
  317. }
  318.  
  319. // Monitor for history changes (when navigating between pages using internal links)
  320. window.addEventListener('popstate', monitorURLChanges);
  321.  
  322. // Monitor for pushState changes (when navigating between pages using internal links)
  323. const originalPushState = history.pushState;
  324. history.pushState = function() {
  325. originalPushState.apply(this, arguments);
  326. monitorURLChanges();
  327. };
  328.  
  329. // Initial check to add/remove buttons based on the current page
  330. monitorURLChanges();
  331. })();