// ==UserScript== // @name Permanent colors // @namespace KrzysztofKruk-FlyWire // @version 0.1.9.2 // @description Permanents colors for segments // @author Krzysztof Kruk // @match https://ngl.flywire.ai/* // @match https://edit.flywire.ai/* // @grant GM_xmlhttpRequest // @grant unsafeWindow // @connect services.itanna.io // @updateURL https://raw.githubusercontent.com/ChrisRaven/FlyWire-Permanent-colors/main/Permanent-colors.user.js // @downloadURL https://raw.githubusercontent.com/ChrisRaven/FlyWire-Permanent-colors/main/Permanent-colors.user.js // @homepageURL https://github.com/ChrisRaven/FlyWire-Permanent-colors // ==/UserScript== if (!document.getElementById('dock-script')) { let script = document.createElement('script') script.id = 'dock-script' script.src = typeof DEV !== 'undefined' && DEV ? 'http://127.0.0.1:5501/FlyWire-Dock/Dock.js' : 'https://chrisraven.github.io/FlyWire-Dock/Dock.js' document.head.appendChild(script) } let wait = setInterval(() => { if (unsafeWindow.dockIsReady) { unsafeWindow.GM_xmlhttpRequest = GM_xmlhttpRequest clearInterval(wait) main() } }, 100) let ids = { supervoxel: null, root: null } let currentColorPatchId = 'permanent-colors-1' function fix_segmentColors_2022_07_15() { if (Dock.ls.get('fix_segmentColors_2022_07_15') === 'fixed') return Object.entries(localStorage).forEach(entry => { if (entry[0].includes('neuroglancerSaveState_v2-')) { let e = JSON.parse(entry[1]) if (e.state && e.state.layers) { e.state.layers.forEach(layer => { if (layer.type === 'segmentation_with_graph' && layer.segmentColors) { layer.segmentColors = {} localStorage.setItem(entry[0], JSON.stringify(e)) } }) } } }) Dock.ls.set('fix_segmentColors_2022_07_15', 'fixed') } function main() { let dock = new Dock() dock.addAddon({ name: 'Permanent colors', id: 'permanent-colors', html: generateHtml(), css: generateCss(), events: { '.permanent-colors-selector': { contextmenu: e => rightClickHandler(e), input: e => { e.preventDefault() updateColor(e) }, change: e => saveColor(e) } } }) let patch = Dock.ls.get('pc-patch-id') if (patch) { currentColorPatchId = patch } ids = getIds() || {} recolorPatches() restoreColors() fix_segmentColors_2022_07_15() } function saveColor(e) { Dock.ls.set('-pc-color-patch-' + e.target.id, e.target.value) } function restoreColors() { for (let i = 1; i <= 4; i++) { let color = Dock.ls.get('-pc-color-patch-permanent-colors-' + i) let patch = document.getElementById('permanent-colors-' + i) if (!color) { switch (i) { // it has to be 6-digits values because of the specs case 1: color = '#FF0000'; break case 2: color = '#00FF00'; break case 3: color = '#0000FF'; break case 4: color = '#FFFF00'; break } } patch.value = color patch.parentElement.style.backgroundColor = color } } function recolorPatches() { document.querySelectorAll('.permanent-colors-wrapper').forEach(el => { el.style.backgroundColor = el.firstChild.value if (el.firstChild.id === currentColorPatchId) { el.classList.add('permanent-colors-wrapper-selected') } }) } function getIds() { return Dock.ls.get('pc-ids', true) } function saveIds() { Dock.ls.set('pc-ids', ids, true) } function changeColor(rootId, color) { function updateColors() { const segmentButton = document.querySelector(`button[data-seg-id="${rootId}"]`) if (!segmentButton) return const colorSelector = segmentButton.parentElement.getElementsByClassName('segment-color-selector')[0] colorSelector.value = color const event = new Event('change') colorSelector.dispatchEvent(event) viewer.layerManager.layersChanged.dispatch() } Dock.addToRightTab('segmentation_with_graph', 'rendering', updateColors) } function updateColor(e) { changeColor(ids.root, e.target.value) e.target.parentElement.style.backgroundColor = e.target.value } function rightClickHandler(e) { e.preventDefault() changeColorByCoords(e) currentColorPatchId = e.target.id Dock.ls.set('pc-patch-id', currentColorPatchId) document.getElementsByClassName('permanent-colors-wrapper-selected')[0].classList.remove('permanent-colors-wrapper-selected') document.getElementById(currentColorPatchId).parentElement.classList.add('permanent-colors-wrapper-selected') } // manual changing function changeColorByCoords(e) { let currentCoords = Dock.getCurrentCoords() let color = e.target.value Dock.getSegmentId(currentCoords[0] * 4, currentCoords[1] * 4, currentCoords[2], (segmentId) => { ids.supervoxel = segmentId Dock.getRootId(segmentId, rootId => rootId && getRootIdCallback(rootId, color)) }) } function getRootIdCallback(rootId, color) { changeColor(rootId, color) ids.root = rootId saveIds() } document.addEventListener('fetch', e => { let response = e.detail.response let url = e.detail.url if (!response || !url) return if (!currentColorPatchId) return // FIXME: sometimes happens for an unknown reason let color = document.getElementById(currentColorPatchId).value if (url.includes('split?') || url.includes('merge?')) { Dock.getRootId(ids.supervoxel, rootId => rootId && getRootIdCallback(rootId, color)) } // new cell has been claimed else if (url.includes('proofreading_drive?')) { ids = { supervoxel: response.supervoxel_id, root: response.root_id } saveIds() Dock.layers.getByType('segmentation_with_graph', false)[0].layer.displayState.segmentStatedColors.clear() // viewer.selectedLayer.layer.layer.displayState.segmentStatedColors.clear() changeColor(ids.root, color) } }) function generateCss() { return /*css*/` .permanent-colors-wrapper { display: inline-block; border: 1px solid #777; background-color: yellow; } .permanent-colors-wrapper-selected { border-color: #eee; } #permanent-colors .permanent-colors-selector { width: 27px; height: 20px; border: none; box-shadow: none; background-color: transparent; position: relative; top: 0; padding: 0; opacity: 0; } ` } function generateHtml() { return /*html*/` ` }