Guest User

Untitled

a guest
Oct 31st, 2026
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.07 KB | None | 0 0
  1.  
  2. // ==UserScript==
  3. // @name EyeWire II - UI Guard
  4. // @namespace local.eyewire.ui
  5. // @version 1.0.0
  6. // @description Fixed segment legend and persistent manual-only Selection Details.
  7. // @match https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
  8. // @run-at document-start
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (() => {
  13. 'use strict';
  14.  
  15. const LEGEND = '.nge-seg-legend';
  16.  
  17. const PANEL =
  18. '.neuroglancer-side-panel.neuroglancer-selection-details';
  19.  
  20. const CLOSE =
  21. '.neuroglancer-icon[title="Close panel"]';
  22.  
  23. const SETTINGS =
  24. 'button.nge-settings-advanced-btn';
  25.  
  26. const STORAGE_KEY = 'ew-ui-guard-selection-closed';
  27.  
  28. let legend = null;
  29. let legendPosition = null;
  30.  
  31. // Restore the user's previous choice after refresh.
  32. // If Selection was manually closed, keep it locked.
  33. let selectionLocked = false;
  34.  
  35. try {
  36. selectionLocked =
  37. localStorage.getItem(STORAGE_KEY) === 'true';
  38. } catch (error) {
  39. console.warn('[EW Guard] Storage unavailable:', error);
  40. }
  41.  
  42. // Manual Settings opening remains allowed.
  43. let manualOpenUntil = 0;
  44.  
  45. function saveSelectionLock(locked) {
  46. selectionLocked = locked;
  47.  
  48. try {
  49. localStorage.setItem(
  50. STORAGE_KEY,
  51. locked ? 'true' : 'false'
  52. );
  53. } catch (error) {
  54. console.warn('[EW Guard] Could not save preference:', error);
  55. }
  56. }
  57.  
  58. // =========================================================
  59. // 1. SEGMENT LEGEND — unchanged from working version
  60. // =========================================================
  61.  
  62. function editingActive() {
  63. return !!document.querySelector(
  64. '.nge-split-merge-overlay, ' +
  65. '.graphene-merge-segments, ' +
  66. '.graphene-multicut'
  67. );
  68. }
  69.  
  70. function findLegend() {
  71. const found = document.querySelector(LEGEND);
  72. if (!found || found === legend) return;
  73. legend = found;
  74. legendPosition = null;
  75. }
  76.  
  77. function captureLegendPosition() {
  78. if (!legend || legendPosition || editingActive()) return;
  79. const rect = legend.getBoundingClientRect();
  80. if (rect.width < 10 || rect.height < 5) return;
  81. legendPosition = {
  82. left: rect.left,
  83. top: rect.top,
  84. width: rect.width
  85. };
  86. console.info('[EW Guard] Legend baseline:', legendPosition);
  87. }
  88.  
  89. function anchorLegend() {
  90. if (!legend || !legendPosition) return;
  91. const p = legendPosition;
  92. legend.style.setProperty('position', 'fixed', 'important');
  93. legend.style.setProperty('left', `${p.left}px`, 'important');
  94. legend.style.setProperty('top', `${p.top}px`, 'important');
  95. legend.style.setProperty('bottom', 'auto', 'important');
  96. legend.style.setProperty('right', 'auto', 'important');
  97. legend.style.setProperty('width', `${p.width}px`, 'important');
  98. legend.style.setProperty('margin', '0', 'important');
  99. legend.style.setProperty('transform', 'none', 'important');
  100. legend.style.setProperty('transition', 'none', 'important');
  101. legend.style.setProperty('z-index', '1000', 'important');
  102. }
  103.  
  104. // =========================================================
  105. // 2. MANUAL SELECTION OPEN / CLOSE
  106. // =========================================================
  107.  
  108. function settingsButtonFrom(target) {
  109. if (!(target instanceof Element)) return null;
  110.  
  111. const button = target.closest(SETTINGS);
  112. if (!button) return null;
  113.  
  114. return /selection\s*details/i.test(
  115. button.textContent || ''
  116. ) ? button : null;
  117. }
  118.  
  119. function closeButtonFrom(target) {
  120. if (!(target instanceof Element)) return null;
  121. return target.closest(`${PANEL} ${CLOSE}`);
  122. }
  123.  
  124. function authorizeManualOpen() {
  125. // Allow the user to open Selection through Settings.
  126. // Do not erase the saved "keep closed" preference:
  127. // it should still apply after the next refresh.
  128. selectionLocked = false;
  129.  
  130. manualOpenUntil = performance.now() + 2000;
  131. }
  132.  
  133. function recordManualClose() {
  134. saveSelectionLock(true);
  135. manualOpenUntil = 0;
  136. }
  137.  
  138. // Capture events before Neuroglancer's own handlers.
  139. document.addEventListener('pointerdown', event => {
  140. if (!event.isTrusted) return;
  141.  
  142. if (settingsButtonFrom(event.target)) {
  143. authorizeManualOpen();
  144. } else if (closeButtonFrom(event.target)) {
  145. recordManualClose();
  146. }
  147. }, true);
  148.  
  149. document.addEventListener('click', event => {
  150. if (!event.isTrusted) return;
  151.  
  152. if (settingsButtonFrom(event.target)) {
  153. authorizeManualOpen();
  154. } else if (closeButtonFrom(event.target)) {
  155. recordManualClose();
  156. }
  157. }, true);
  158.  
  159. document.addEventListener('keydown', event => {
  160. if (!event.isTrusted) return;
  161.  
  162. if (event.key !== 'Enter' && event.key !== ' ') return;
  163.  
  164. if (settingsButtonFrom(event.target)) {
  165. authorizeManualOpen();
  166. } else if (closeButtonFrom(event.target)) {
  167. recordManualClose();
  168. }
  169. }, true);
  170.  
  171. // =========================================================
  172. // 3. BLOCK UNWANTED SELECTION PANEL INSERTION
  173. // =========================================================
  174.  
  175. function containsSelectionPanel(node) {
  176. if (!(node instanceof Element)) return false;
  177.  
  178. return (
  179. node.matches(PANEL) ||
  180. !!node.querySelector(PANEL)
  181. );
  182. }
  183.  
  184. function shouldBlock(node, receiver) {
  185. if (!selectionLocked) return false;
  186.  
  187. if (performance.now() < manualOpenUntil) {
  188. return false;
  189. }
  190.  
  191. if (!containsSelectionPanel(node)) {
  192. return false;
  193. }
  194.  
  195. const parent =
  196. receiver instanceof Element
  197. ? receiver
  198. : null;
  199.  
  200. if (!parent) return false;
  201.  
  202. return (
  203. parent.matches('.neuroglancer-side-panel-column') ||
  204. !!parent.closest('.neuroglancer-side-panel-column')
  205. );
  206. }
  207.  
  208. const originalInsertBefore = Node.prototype.insertBefore;
  209.  
  210. Node.prototype.insertBefore = function(node, reference) {
  211. if (shouldBlock(node, this)) {
  212. return node;
  213. }
  214.  
  215. return originalInsertBefore.call(this, node, reference);
  216. };
  217.  
  218. const originalAppendChild = Node.prototype.appendChild;
  219.  
  220. Node.prototype.appendChild = function(node) {
  221. if (shouldBlock(node, this)) {
  222. return node;
  223. }
  224.  
  225. return originalAppendChild.call(this, node);
  226. };
  227.  
  228. // =========================================================
  229. // 4. LEGEND WATCHER
  230. // =========================================================
  231.  
  232. let updatePending = false;
  233.  
  234. function update() {
  235. if (updatePending) return;
  236. updatePending = true;
  237.  
  238. requestAnimationFrame(() => {
  239. updatePending = false;
  240. findLegend();
  241. captureLegendPosition();
  242. anchorLegend();
  243. });
  244. }
  245.  
  246. function start() {
  247. const observer = new MutationObserver(update);
  248.  
  249. observer.observe(document.documentElement, {
  250. childList: true,
  251. subtree: true
  252. });
  253.  
  254. update();
  255.  
  256. setInterval(update, 500);
  257. }
  258.  
  259. if (document.readyState === 'loading') {
  260. document.addEventListener('DOMContentLoaded', start, {
  261. once: true
  262. });
  263. } else {
  264. start();
  265. }
  266. })();
  267.  
Advertisement
Add Comment
Please, Sign In to add comment