Roblox 2016 Item Viewer

Restores the look of the item viewer to look more like how it did in 2016

  1. // ==UserScript==
  2. // @name Roblox 2016 Item Viewer
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description Restores the look of the item viewer to look more like how it did in 2016
  6. // @author Xx_3mdiv1der78_xX [AKA: The_Noise.]
  7. // @match https://www.roblox.com/catalog/*
  8. // @match https://www.roblox.com/bundles/*
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const style = document.createElement('style');
  17. style.textContent = `
  18. /* White box for item details with a border */
  19. .item-details-wrapper {
  20. background: #fff !important;
  21. border: 10px solid #FFFFFF !important;
  22. border-radius: 3px !important;
  23. padding: 12px !important;
  24. margin: 12px auto !important;
  25. box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
  26. position: relative;
  27. }
  28.  
  29. /* Thumbnail outline */
  30. .thumbnail-holder {
  31. border: 1px solid #B8B8B8 !important;
  32. border-radius: 3px !important;
  33. padding: 3px !important;
  34. }
  35.  
  36. /* Recommendations styling */
  37. .item-card-caption .recommended-creator {
  38. margin-top: 3px !important;
  39. font-size: 12px !important;
  40. }
  41. .item-card-caption .recommended-creator-by {
  42. margin-right: 3px !important;
  43. color: #666 !important;
  44. }
  45. .item-card-caption .recommended-creator .xsmall {
  46. font-size: 12px !important;
  47. line-height: 14px !important;
  48. }
  49. .item-card-caption .recommended-creator .text-link {
  50. color: #00A2FF !important;
  51. text-decoration: none !important;
  52. }
  53. .item-card-caption .recommended-creator .text-label {
  54. color: #666 !important;
  55. font-weight: normal !important;
  56. }
  57.  
  58. /* Recommendations header */
  59. .item-list-carousel-title h1.font-header-1 {
  60. font-size: 20px !important;
  61. font-weight: 400 !important;
  62. color: #333 !important;
  63. }
  64.  
  65. /* BTR Preview Container Styles */
  66. #item-thumbnail-container-frontend .thumbnail-2d-container {
  67. position: relative !important;
  68. }
  69.  
  70. #item-thumbnail-container-frontend .thumbnail-2d-container .btr-preview-container-itempage {
  71. position: absolute !important;
  72. top: 0 !important;
  73. left: 0 !important;
  74. width: 100% !important;
  75. height: 100% !important;
  76. z-index: 1 !important;
  77. }
  78.  
  79. /* Ensure BTR preview stays visible */
  80. #item-thumbnail-container-frontend .btr-preview-container-itempage {
  81. display: block !important;
  82. visibility: visible !important;
  83. opacity: 1 !important;
  84. }
  85.  
  86. /* Ensure the canvas stays visible */
  87. #item-thumbnail-container-frontend .btr-preview-container canvas {
  88. display: block !important;
  89. visibility: visible !important;
  90. opacity: 1 !important;
  91. }
  92. `;
  93. document.head.appendChild(style);
  94.  
  95. function updateStyles() {
  96. // Only remove empty item-details-wrapper elements
  97. const existingWrappers = document.querySelectorAll('.item-details-wrapper');
  98. existingWrappers.forEach(wrapper => {
  99. // Check if this wrapper contains the item content
  100. const hasThumbnail = wrapper.querySelector('#item-thumbnail-container-frontend');
  101. const hasInfo = wrapper.querySelector('#item-info-container-frontend');
  102.  
  103. // Only remove if it doesn't contain the important content
  104. if (!hasThumbnail && !hasInfo) {
  105. wrapper.remove();
  106. }
  107. });
  108.  
  109. // Check if we already have a wrapper with content
  110. const existingContentWrapper = document.querySelector('.item-details-wrapper #item-thumbnail-container-frontend');
  111. if (!existingContentWrapper) {
  112. // Create a new wrapper only if we don't have one with content
  113. const itemDetailsWrapper = document.createElement('div');
  114. itemDetailsWrapper.className = 'item-details-wrapper';
  115.  
  116. // Move the relevant elements into the new wrapper
  117. const thumbnailContainer = document.querySelector('#item-thumbnail-container-frontend');
  118. const infoContainer = document.querySelector('#item-info-container-frontend');
  119. const socialContainer = document.querySelector('.item-social-container');
  120.  
  121. if (thumbnailContainer && infoContainer) {
  122. itemDetailsWrapper.appendChild(thumbnailContainer);
  123. itemDetailsWrapper.appendChild(infoContainer);
  124. if (socialContainer) {
  125. itemDetailsWrapper.appendChild(socialContainer);
  126. }
  127. // Insert the new wrapper into the page
  128. const pageContent = document.querySelector('.page-content');
  129. if (pageContent) {
  130. pageContent.insertBefore(itemDetailsWrapper, pageContent.firstChild);
  131. }
  132. }
  133. }
  134.  
  135. // Handle BTR thumbnails
  136. function positionBTRThumbnail() {
  137. const btrPreview = document.querySelector('.btr-preview-container-itempage');
  138. const thumbnail2dContainer = document.querySelector('#item-thumbnail-container-frontend .thumbnail-2d-container');
  139.  
  140. if (btrPreview && thumbnail2dContainer && !thumbnail2dContainer.contains(btrPreview)) {
  141. // Move the BTR preview into the 2D thumbnail container specifically
  142. thumbnail2dContainer.appendChild(btrPreview);
  143.  
  144. // Ensure the preview stays visible
  145. btrPreview.style.display = 'block';
  146. btrPreview.style.visibility = 'visible';
  147. btrPreview.style.opacity = '1';
  148.  
  149. // Ensure the canvas stays visible
  150. const canvas = btrPreview.querySelector('canvas');
  151. if (canvas) {
  152. canvas.style.display = 'block';
  153. canvas.style.visibility = 'visible';
  154. canvas.style.opacity = '1';
  155. }
  156. }
  157. }
  158.  
  159. // Remove the item-details-container if it exists
  160. const detailsContainer = document.querySelector('.item-details-container');
  161. if (detailsContainer) {
  162. detailsContainer.remove();
  163. }
  164.  
  165. // Update creator sections
  166. const creatorDivs = document.querySelectorAll('.item-card-caption .item-card-creator:not(.recommended-creator)');
  167. creatorDivs.forEach(div => {
  168. div.classList.add('recommended-creator');
  169.  
  170. const span = div.querySelector('span');
  171. const link = div.querySelector('a');
  172.  
  173. if (!span || !link) return;
  174.  
  175. const byLabel = document.createElement('span');
  176. byLabel.className = 'xsmall text-label recommended-creator-by';
  177. byLabel.textContent = 'By';
  178.  
  179. link.className = 'xsmall text-overflow text-link ng-binding';
  180.  
  181. span.textContent = '';
  182. span.append(byLabel, ' ', link);
  183. });
  184.  
  185. // Update Recommendations header
  186. const header = document.querySelector('.item-list-carousel-title h1.font-header-1');
  187. if (header && header.textContent.trim() === 'Recommendations') {
  188. header.textContent = 'Recommended Items';
  189. }
  190.  
  191. // Initial BTR positioning
  192. positionBTRThumbnail();
  193.  
  194. // Add observer for thumbnail changes
  195. const thumbnailObserver = new MutationObserver(() => {
  196. positionBTRThumbnail();
  197. });
  198.  
  199. // Start observing the thumbnail container
  200. const thumbnailTarget = document.querySelector('#item-thumbnail-container-frontend');
  201. if (thumbnailTarget) {
  202. thumbnailObserver.observe(thumbnailTarget, {
  203. childList: true,
  204. subtree: true,
  205. attributes: true,
  206. characterData: true
  207. });
  208. }
  209. }
  210.  
  211. // Run on page load and watch for changes
  212. let timeout;
  213. const observer = new MutationObserver(() => {
  214. clearTimeout(timeout);
  215. timeout = setTimeout(updateStyles, 100);
  216. });
  217.  
  218. function initObserver() {
  219. const container = document.querySelector('.item-list-carousel');
  220. if (container) {
  221. updateStyles();
  222. observer.observe(container, {
  223. childList: true,
  224. subtree: true
  225. });
  226. } else {
  227. setTimeout(initObserver, 100);
  228. }
  229. }
  230.  
  231. if (document.readyState === 'loading') {
  232. document.addEventListener('DOMContentLoaded', initObserver);
  233. } else {
  234. initObserver();
  235. }
  236. })();