您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Increase productivity by filtering your list of tasks. Requires pomofocus premium plan.
// ==UserScript== // @name Pomofocus Projects Filter // @namespace http://tampermonkey.net/ // @version 1.0 // @description Increase productivity by filtering your list of tasks. Requires pomofocus premium plan. // @author mat k // @match https://pomofocus.io/* // @icon https://www.google.com/s2/favicons?sz=64&domain=pomofocus.io // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; function getElementByXpath(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } function refreshTaskList(taskNodes) { console.log(taskNodes) let tree = analyseNodeTree(taskNodes) document.querySelector('div[class="filter-div"]')?.remove() renderFilterButtons(tree) return tree } function analyseTreeForProjectNames(tree) { let projects = [] tree.forEach(object => { if (projects.includes(object.project)) { } else if (object.project == undefined) { } else { projects.push(object.project) } }) projects.sort((a, b) => a.localeCompare(b)) projects.splice(0,0,'Unassigned') projects.splice(0,0,'All') return projects } // Function to render the filter buttons using project names in the projectTasks object function styledButton(btn) { btn.style.cursor = 'pointer' btn.style.border = 'none' btn.style.margin = '10px 5px' btn.style.padding = '0 12px' btn.style.borderRadius = '4px' btn.style.boxShadow = 'rgb(235 235 235) 0 6px 0' btn.style.fontFamily = 'ArialRounded' btn.style.fontSize = '16px' btn.style.minHeight = '22px' btn.style.fontWeight = 'bold' //btn.style.width = '100px' btn.style.color = 'rgb(186, 73, 73)' btn.style.backgroundColor = 'white' btn.style.transition = 'color 0.5s ease-in-out 0s' btn.style.whiteSpace = 'nowrap' btn.style.overflow = 'clip' btn.style.textOverflow = 'ellipsis' btn.style.flex = '1 1 30%' return btn } function renderFilterButtons(tree) { let projectTasks = analyseTreeForProjectNames(tree) let filterDiv = document.createElement('div') filterDiv.style.display = 'flex' filterDiv.style.flexDirection = 'column' filterDiv.style.marginBottom = '14px' filterDiv.classList.add('filter-div') let filterTitle = document.createElement('div') filterTitle.innerText = 'Filter by Project' filterTitle.style.fontFamily = 'ArialRounded' filterDiv.appendChild(filterTitle) let btnStatic = document.createElement('div') btnStatic.style.display = 'flex' filterDiv.appendChild(btnStatic) let btnDynamic = document.createElement('div') btnDynamic.style.display = 'flex' btnDynamic.style.flexWrap = 'wrap' filterDiv.appendChild(btnDynamic) for (let index in projectTasks) { projectTasks[index] console.log() let filterButton = document.createElement('button') filterButton = styledButton(filterButton) filterButton.innerText = projectTasks[index] if (index == 0 | index == 1) { btnStatic.appendChild(filterButton) } else { btnDynamic.appendChild(filterButton) } filterButton.addEventListener('click', function(e) { // Toggle the visibility of tasks for the selected project if (index == 0) { tree.forEach(taskObject => { taskObject.parentNode.style.display = 'block' }) } else if (index == 1) { tree.forEach(taskObject => { taskObject.parentNode.style.display = 'block' if (taskObject.project == undefined) { } else { taskObject.parentNode.style.display = 'none' } }) } else { tree.forEach(taskObject => { taskObject.parentNode.style.display = 'block' if (taskObject.project == e.target.outerText) { } else { taskObject.parentNode.style.display = 'none' } }) } }) } // Add the filter div above the task list taskDiv.insertBefore(filterDiv, taskDiv.firstChild) } function analyseNodeTree(tree) { const objectArray = [] tree.childNodes.forEach(task => { // gets metadata about tasks in the task list. // it is a bit overkill, but left here in case of future development let wholeTask = task.childNodes[0].childNodes let description if (wholeTask.length > 1) { description = wholeTask[1].childNodes[0].childNodes[0].data } // taskHeader = [div = taskOverview, div = activityPomos] let taskHeader = wholeTask[0].childNodes // taskOverview = [div = checkBox, div = details] let taskOverview = taskHeader[0].childNodes let checkBox = taskOverview[0] // details = [span = project, text, text = title] let details = taskOverview[1].childNodes let project = details[0].childNodes[0]?.data let title = details[2].data // pomos = [div = current, div, div=target] let pomos = taskHeader[1].childNodes[0].childNodes let currentPomos = pomos[0].data let targetPomos = pomos[1].childNodes[1].data let object = { parentNode: task, title: title, project: project, description: description, currentPomos: currentPomos, targetPomos: targetPomos, } objectArray.push(object) }) return objectArray } let taskDiv let taskNodes let waitForTaskList = setInterval(function() { taskDiv = getElementByXpath("//*[@id='target']/div/div[1]/div[2]/div/div[3]") if (taskDiv) { taskNodes = taskDiv.childNodes[1] clearInterval(waitForTaskList); refreshTaskList(taskNodes); // Observe the task list for changes let taskListObserver = new MutationObserver(() => refreshTaskList(taskNodes)); taskListObserver.observe(taskNodes, {childList: true}); } }, 200); setTimeout(function() { clearInterval(waitForTaskList); }, 2000); // Your code here... })();