-
-
Save NHZEX/b273163f6981c452f72bbb06be3efd77 to your computer and use it in GitHub Desktop.
Tabs Outliner: kill duplicate windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| async function scanDupes() { | |
| let dupes = {}; | |
| for (const nod of treeView.treeModel.currentSession_rootNode.subnodes) { // only check root level nodes, don't do complex recursive checks for "sub-roots" | |
| const strbld = []; | |
| nodeterate(nod, strbld); | |
| const crashDetectedDate = nod.chromeWindowObj.crashDetectedDate | |
| ? new Date(nod.chromeWindowObj.crashDetectedDate).toLocaleString() | |
| : 'not crash'; | |
| console.log(nod.id, crashDetectedDate, nod); | |
| } | |
| } | |
| await scanDupes(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // cyrb53 stolen from https://stackoverflow.com/a/52171480 | |
| const cyrb53 = function(str, seed = 0) { | |
| let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; | |
| for (let i = 0, ch; i < str.length; i++) { | |
| ch = str.charCodeAt(i); | |
| h1 = Math.imul(h1 ^ ch, 2654435761); | |
| h2 = Math.imul(h2 ^ ch, 1597334677); | |
| } | |
| h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909); | |
| h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909); | |
| return 4294967296 * (2097151 & h2) + (h1>>>0); | |
| }; | |
| async function digestMessage(message) { | |
| const msgUint8 = new TextEncoder().encode(message); // 编码为(utf-8)Uint8Array | |
| const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // 计算消息的哈希值 | |
| const hashArray = Array.from(new Uint8Array(hashBuffer)); // 将缓冲区转换为字节数组 | |
| const hashHex = hashArray | |
| .map((b) => b.toString(16).padStart(2, "0")) | |
| .join(""); // 将字节数组转换为十六进制字符串 | |
| return hashHex; | |
| } | |
| function nodeterate(currentNode, stringBuilder) { | |
| if (currentNode.type == "separatorline") stringBuilder.push("separatorline"); | |
| else if (currentNode.type == "textnote") stringBuilder.push(`textnote ${currentNode?.persistentData?.note}`); | |
| else if (currentNode.type == "win" || currentNode.type == "savedwin") stringBuilder.push("window"); | |
| else stringBuilder.push(currentNode.getHref()); | |
| for (const sn of currentNode.subnodes) { | |
| nodeterate(sn, stringBuilder); | |
| } | |
| } | |
| function nukeNodeArray(nodeArray) { | |
| const DUMMY = false; | |
| for (const currentNode of nodeArray) { | |
| if (currentNode.type !== "win" && currentNode.type !== "savedwin") { | |
| console.log("this node should not have almost died:"); | |
| console.log(currentNode); | |
| continue; | |
| } | |
| if (DUMMY) { | |
| console.log("would now kill:"); | |
| console.log(currentNode); | |
| } else { | |
| console.log("removing node:"); | |
| const crashDetectedDate = currentNode.chromeWindowObj.crashDetectedDate | |
| ? new Date(currentNode.chromeWindowObj.crashDetectedDate).toLocaleString() | |
| : 'not crash'; | |
| console.log(currentNode.id, crashDetectedDate, currentNode); | |
| currentNode.removeOwnTreeFromParent(); | |
| } | |
| } | |
| } | |
| async function killDupes(killActive = false) { | |
| let dupes = {}; | |
| for (const nod of treeView.treeModel.currentSession_rootNode.subnodes) { // only check root level nodes, don't do complex recursive checks for "sub-roots" | |
| const strbld = []; | |
| nodeterate(nod, strbld); | |
| // const hash = cyrb53(strbld.join()); | |
| const hash = await digestMessage(strbld.join()) | |
| if (!dupes.hasOwnProperty(hash)) { | |
| dupes[hash] = [] | |
| } | |
| dupes[hash].push(nod); | |
| } | |
| console.log('total nodes', treeView.treeModel.currentSession_rootNode.subnodes.length); | |
| console.log('total hash', Object.keys(dupes).length) | |
| const summary = Object.keys(dupes).map((key) => [key, dupes[key].length, dupes[key]]); | |
| console.log('hash nodes size: ', summary); | |
| // summary.forEach((v) => { | |
| // console.log(v[0], v[1]) | |
| // }) | |
| for (const duperino of Object.values(dupes)) { | |
| if (duperino.length < 2) continue; | |
| const inactiveNodes = duperino.filter(x => x.type !== "win"); // keep loaded window(s) alive | |
| if (inactiveNodes.length < duperino.length) { | |
| if ((duperino.length - inactiveNodes.length) > 1) { | |
| console.log("WARNING: duplicate active windows ->"); | |
| for (const loadedWindow of duperino.filter(x => x.type == "win")) console.log(loadedWindow); | |
| } | |
| nukeNodeArray(inactiveNodes); | |
| if (killActive) nukeNodeArray(duperino.filter(x => x.type == "win").slice(1)); | |
| } | |
| else { // keep oldest | |
| nukeNodeArray(duperino.slice(1)); | |
| } | |
| } | |
| } | |
| await killDupes(); | |
| // to also prune duplicate *loaded* windows down to a single one, try: killDupes(true); | |
| // note that this will disregard how many subnodes are loaded! |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tabs Outliner 在哪里存储自己的数据以及如何从备份恢复或将树移动到新电脑。
https://groups.google.com/g/tabs-outliner-support-group/c/eKubL9Iw230
原始内容