когда не надо очищать
Да вот я уже и думаю, может оставить всё как сейчас есть.
Выбрал поисковую систему, строка не очистилась, тут же можно выбрать другой поиск.
Ну и на худой конец можно колёсиком мышки почистить
Отсутствует
Dumby
xrun1 пишет
Можно сделать тоже самое для ucf, тоько для ПКМ сделать эмуляцию, как СКМ с вставкой из буфера?
А можно сделать, чтобы текст из буфера в AkelPad открывался?
Отредактировано Garalf (22-07-2021 19:28:44)
Отсутствует
Dumby
А нельзя ли добавить как-то в этот скрипт вот этот код(второй спойлер)? Добавьте пожалуйста, если возможно.
Отсутствует
Garalf
Вроде есть такое в документации. Демо-кнопка.
(async self => CustomizableUI.createWidget(self = { label: "AkelPad", tooltiptext: "ЛКМ: AkelPad\nПКМ: AkelPad + вставка из буфера", akelpadPath: "C:\\Program Files\\AkelPad\\AkelPad.exe", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAgAAAAApKSlRRkZGh0BAQHxBQUF+QUFBfkFBQX5BQUF+QEBAfEZGRogaGho1AAAAAAABAAECAgICAgICAgQEBAUAAAAAMTEwWeLi4f//////+vr6//r6+v/6+vr//Pz8//v7+//5+fn++/v7/7CwsPdNTU2hAQEBAgAAAAAAAAAAAwMDBAAAAABvbm+x8vHy/+Df4Pfi4+P76ubr++Lh4vvk5OT7/f39+/////v////77/Dv/aysrP8BEAElKngnxDCDLdEvgSzOLX8qy0GTPu1Zq1b/WatW/lasU/9op2f/xcPF/8PEw//i5+L/8vfx//H27//p6+n/qqqq/ESaQOS67a7/teip/rbpqv+36qv/suSm/6zfoP+s36D/teOo/4bNfP93s3b/39bf/9XY1f/5/Pj/+Pv2/+vs6/+qqar/T6RK8bvjsP6t2qT8sNym/LDcpvyx3af+st6o/7LeqP+w3ab/vuaz/1mqVP+2w7b/3dvd//D07//4+/f/6+zr/6qqqv8BNwFvndiU/8Djt/y34a//ueGw/7nhsP+54bD/ueGw/7jgr//B5Lj/mtSS/3q1ev/WzNb/4ufh//f79f/p6+n/qqqq/wAAAANHk0Pbzu/F/73gtP3A47f/wOO3/8Djt//A47f/wOO4/73itf/D5rn/Y7Je/7/MwP/k4eP/+f35/+vs6/+qqqr/AAAAAAAzAGap4KP/zebG+8Tkvv/G5b//xuS//8blv//F5b7/vuK2/8LkuP+U0Yv/ZaBl/97W3v/u9e3/6+3r/6qqqv8DAQMAAAAABE6XS+Db8dT/yuPD/czmxv/N5sf/zObG/8blv//A47j/t+Cu/7vjsP9nuWP/uMW5/+zr6//s7+z/qamq/wACAAQAAAAAATQBZrTir//a6dX70ufN/9Pozv/N5sf/xuS//8Djt/+44K//u+Ow/4zNgv9uqW7/6+Tr/+ru6v+qqar/AAAAAQMBAwAAAAAHVJhS2Or25v/W59L91OjP/8zmxv/G5b//wOO3/7rhsP+w3ab/tuSp/1isU/+vva//6+rq/6qqqv8AAAAAAQIBAwAAAAAENQRkv+S7/+Pr3vvR58z/zObG/8bkv/+/4rf/ueCw/7Hep/+z4Kf/h818/3Kucv/r4uv/qKqo/gAAAAAAAAABAwEDAAAAAApVl1TU7Pbo/9HlzP3P58j/xuS//8Ljuf+64bH/s9+p/6vcoP+t4KD/Wq5U/8bSx/23tLf9AAAAAAAAAAABAgEDAAAAAAk3CWaq36b/1unR+cLjvPzF5r78t+Cv/LXgrP2w36b8odmW/Kzgn/1yxGf7gLKA/52Sne0AAAAAAAAAAAAAAAECAQIAAAEADUOCQcdwt23/Zati82GrXvVjrWD4XKlY9FqpVvZaqlX3UqZN81OnTvxYbFndGxcbNAAAAAAAAAAAAAAAAAABAQEAAAAAFBAUHTAzMGcqKipRJSclUS8wL14nJyZNKCspVy4uLlsiJCJKNjU2aQQABAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", id: "ucf-AkelPad", localized: false, onCreated(btn) { btn.setAttribute("image", this.image); btn._handleClick = btn.oncontextmenu = this.akelpad; }, akelpad(e) { if (e && (e.ctrlKey || e.shiftKey)) return; var akelpad = Components.Constructor( "@mozilla.org/file/local;1", "nsIFile", "initWithPath" )(self.akelpadPath); if (!e) return akelpad.launch(); e.preventDefault(); var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(akelpad); var args = [ //"/NewInstance", // Форсировать создание нового экземпляра программы //"/Command(4157)", // Выделить всё "/Command(4155)", // Вставить ]; process.runwAsync(args, args.length); } }))();
Добавьте пожалуйста
Давай сначала что-то более простецкое попробуем,
если не сработает, тогда уже USER_SHEET и всё такое.
Вот, добавил строку, задающую для ноды
CSS-переменную --urlbar-icon-fill-opacity (в атрибут style пропишется).
(async (url, pa = ChromeUtils.import(url).PageActions) => pa.addAction(new pa.Action({ title: "Копировать ссылку", tooltip: "Копировать ссылку", iconURL: "chrome://browser/skin/link.svg", id: "ucf-copyURL", pinnedToUrlbar: true, onCommand(e) { var gBrowserBundle = { GetStringFromName: () => "Скопировано в буфер обмена!" }; var show = eval(`(function ${e.view.ConfirmationHint.show})`); var helper = Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper); (this.onCommand = e => { var win = e.view; var uri = win.gBrowser.selectedBrowser.currentURI; helper.copyString(win.gURLBar.makeURIReadable(uri).displaySpec); var anchor = win.BrowserPageActions.panelAnchorNodeForAction(this, e); show.call(win.ConfirmationHint, anchor, "", {event: e, hideArrow: true}); })(e); }, onPlacedInUrlbar: node => node.style.setProperty("--urlbar-icon-fill-opacity", ".6", "important") })))("resource:///modules/PageActions.jsm");
Отсутствует
Dumby
Здравствуйте, вы бы не могли помочь, если можно так сделать, чтобы левый клик по крестику закрытия окна выполнял функцию выхода с браузера (ctrl+shift+q) а правый клик стандартно закрывал окно?
Отредактировано Stkvsky (31-07-2021 21:08:50)
Отсутствует
клик по крестику закрытия окна
Смотря по какому. Если по одному из трёх лисьих, то вроде так работает.
А если по тому, который в нативном titlebar'е окна Windows,
то что-то не получается придумать ничего хорошего.
(sel => { var lst = e => { if (e.button == 1 || e.ctrlKey || e.shiftKey) return; e.preventDefault(); e.button ? BrowserTryToCloseWindow(e) : goQuitApplication(e); } for(var btn of gNavToolbox.querySelectorAll(sel)) addEventListener("click", lst, false, btn); })("#close-button, hbox.titlebar-buttonbox-container toolbarbutton.titlebar-close");
Отсутствует
Dumby
Есть проблемка и по ходу оне не решатся.. Label - отрегулировать . ЭТИ СПОСОБЫ НЕ ПОДХОДЯТ
.bookmark-item:hover { background-image: url( chrome://browser/skin/tabbrowser/tab-bg-active.png); background-repeat: repeat-x !important; padding: 2px 0px 2px 2px !important; background-position: left center !important; border-radius: 3px !important; background-color: #FFFF80 !important; color: blue !important; }
Отсутствует
Dumby
Можно еще вас попросить изменить скрипт, который вы делали для открытия папки закладок в контейнере (ПКМ по папке закладок - Открыть все в контейнере) (после открытия папка закладок удаляется)
(async (sel, self) => ({ icon: "circle", colors: [ "#FF9800", "#03A9F4", "#FFC107", "#00BCD4", "#FFEB3B", "#009688", "#CDDC39", "#4CAF50", "#8BC34A", "#D32F2F", "#4949ff", "#C2185B", "#607D8B", "#7B1FA2", "#9E9E9E", "#673AB7", "#795548", "#3F51B5", "#FF5722", "#2196F3", ], initColors() { var colorName = "ucf-gen"; var css = "@-moz-document url(about:preferences#containers)," + " url-prefix(chrome://browser/content/browser.x) {\n"; this.colors.forEach((color, ind) => { var [ic, tc] = color.split(/\s*\|\s*/); css += `\t.identity-color-${colorName}${ind} {\n` + `\t\t--identity-tab-color: ${tc || ic};\n` + `\t\t--identity-icon-color: ${ic};\n\t}\n` }); var url = "data:text/css;charset=utf-8," + encodeURIComponent(css + "}"); var sss = Cc["@mozilla.org/content/style-sheet-service;1"] .getService(Ci.nsIStyleSheetService); sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET); var len = this.colors.length; var pref = "ucf.openInGeneratedContainer.lastColor"; var ind = Math.min(Services.prefs.getIntPref(pref, -1), len - 1); this.nextColor = () => { var next = ind + 1; Services.prefs.setIntPref(pref, ind = next == len ? 0 : next); return colorName + ind; } }, quit: false, init(topic) { Services.obs.addObserver(self = this, topic); var lt = "browser-lastwindow-close-granted"; var lw = () => this.quit = true; Services.obs.addObserver(lw, lt); Services.obs.addObserver(function quit(s, t) { self.quit = true; Services.obs.removeObserver(self, topic); Services.obs.removeObserver(lw, lt); Services.obs.removeObserver(quit, t); }, "quit-application-granted"); this.initColors(); this.newUsercontext = name => { var id = this.cis.create( name || `[ ${this.cis._lastUserContextId + 1} ]`, this.icon, this.nextColor() ).userContextId; this.saveGens(this.gens.add(id)); return id; } var cpref = "ucf.openInGeneratedContainer.containers"; var arr = Services.prefs.getStringPref(cpref, "").split(",").map(Number).filter(Boolean); if (arr.length) { var ids = this.cis.getPublicIdentities().map(i => i.userContextId); arr = arr.filter(id => ids.includes(id)); } this.gens = new Set(arr); (this.saveGens = () => Services.prefs.setStringPref(cpref, Array.from(this.gens).join(",")))(); }, observe(doc) { var list = doc.querySelectorAll(sel); if (!list.length) return; var menuitem = doc.createXULElement("menuitem"); for(var args of Object.entries({ selectiontype: "single", oncommand: "cmd(window)", nodetype: "folder|query", selection: "folder|query", label: "Открыть всё в контейнере", id: "placesContext_openContainer:tabs:newUsercontext" })) menuitem.setAttribute(...args); menuitem.cmd = this.cmd; menuitem.rnd = menuitem.constructor.prototype.render; menuitem.render = this.render; var [m1, m2] = menuitem.list = Array.from(list); (m2 || m1).after(menuitem); if (doc.documentElement.getAttribute("windowtype") != "navigator:browser") return; for(var btn of [ doc.getElementById("tabs-newtab-button"), doc.getElementById("new-tab-button") || doc.ownerGlobal.gNavToolbox.palette.querySelector("#new-tab-button") ]) if (btn) btn.checkForMiddleClick = this.click; var win = doc.ownerGlobal; this.redefDoSearch(win, win.customElements.get("searchbar").prototype); win.gBrowser.tabContainer.addEventListener("TabClose", this.tabClose); win.addEventListener("unload", this.winUnload, {once: true}); this.quit = false; }, winUnload(e) { var win = e.target.ownerGlobal; win.removeEventListener("TabClose", self.tabClose); if (self.quit) return; var gb = win.gBrowser; if (gb) for(var tab of gb.tabs) self.tabClose(null, tab); }, closed: new Set(), cis: ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm") .ContextualIdentityService, tabClose(e, tab = e.target) { var id = +tab.getAttribute("usercontextid"); id && self.gens.has(id) && self.closed.add(id); self.closed.size == 1 && ChromeUtils.idleDispatch(self.meaybeRemove); }, meaybeRemove() { var ids = Array.from(self.closed); self.closed.clear(); for(var id of ids) self.meaybeRemoveById(id); }, meaybeRemoveById(id) { for(var win of CustomizableUI.windows) if (win.document.querySelector(`tab.tabbrowser-tab[usercontextid="${id}"]`)) return; this.saveGens(this.gens.delete(id)); this.cis.remove(id); }, redefDoSearch(win, proto) { var code = `(openTrustedLinkIn => [ {${proto.doSearch}}, openTrustedLinkIn ])( function otl(url, where, params) { /* if (where != "current") */ if (where != "current") params.resolveOnNewTabCreated = br => gBrowser.moveTabTo( gBrowser.getTabForBrowser(br), Infinity ), params.userContextId = otl.newUsercontext( document.getElementById("searchbar").value ); openTrustedLinkIn(url, where, params); } );`; (this.redefDoSearch = (win, proto) => { var [obj, func] = win.eval(code); Object.assign(proto, obj); func.newUsercontext = this.newUsercontext; })(win, proto); }, click(btn, e) { if (!(e.button != 2 || e.ctrlKey || e.shiftKey)) { var txt = e.view.readFromClipboard(); if (txt) { var urls = txt.split("\n").map(self.map).filter(Boolean); if (urls.length) return e.preventDefault(), self.openFromClipboard(e.view, urls); } } e.view.checkForMiddleClick(btn, e); }, eo: Object.create(null), map(str) { str = str.trim(); try { var scheme = Services.io.extractScheme(str); var ph = Services.io.getProtocolHandler(scheme); if (ph.scheme == scheme) return Services.io.newURI(str) && {uri: str}; } catch {} }, openFromClipboard(win, urls) { if (win.OpenInTabsUtils.confirmOpenInTabs(urls.length, win)) urls.load = true, this.open(win, this.eo, urls); }, async render() { this.rnd(); await new Promise(this.ownerGlobal.requestAnimationFrame); this.hidden || (this.hidden = this.list.every(self.every)); }, every: node => node.disabled || node.hidden, cmd(win) { var view = this.parentNode._view; var node = win.document.popupNode; node = node._placesView && node._placesView.result.root; self.open(win, node || view.selectedNode || view.result.root); }, open(win, node, list) { var gbw = Cu.import("resource:///modules/PlacesUIUtils.jsm", {}).getBrowserWindow; var w = gbw(win); this.pu = w.PlacesUIUtils; this.fs = w.PlacesUtils.favicons; this.sysp = w.E10SUtils.SERIALIZED_SYSTEMPRINCIPAL; (this.open = (win, node, list) => { this.openURLs(gbw(win), list || win.PlacesUtils.getURLsForContainerNode(node)); node.bookmarkGuid && this.pu.doCommand(win, "placesCmd_delete"); })(win, node, list); }, async openURLs(win, urls) { var userContextId = this.newUsercontext(); var mark = !win.PrivateBrowsingUtils.isWindowPrivate(win); var {load} = urls, gb = win.gBrowser, pos = gb.selectedTab._tPos; for(var {uri, title, isBookmark} of urls) try { if (mark) isBookmark ? this.pu.markPageAsFollowedBookmark(uri) : this.pu.markPageAsTyped(uri); if (load) { gb.addTrustedTab(uri, {index: ++pos, userContextId}); continue; } var state = {userContextId, entries: [{ url: uri, title: title || uri, triggeringPrincipal_base64: this.sysp }]}; var [,, data, mime] = await new Promise( resolve => this.fs.getFaviconDataForPage( Services.io.newURI(uri), (...args) => resolve(args), 16 ) ); if (data.length) state.image = `data:${ mime || "image/x-icon" };base64,${ btoa(String.fromCharCode(...data)) }`; var tab = gb.addTrustedTab(null, {index: ++pos, userContextId}); win.SessionStore.setTabState(tab, state); } catch {}; } }).init("chrome-document-loaded"))( "#placesContext_openBookmarkContainer\\:tabs,#placesContext_openContainer\\:tabs" );
Вы бы не могли пожалуйста добавить чтобы названия для контейнера бралось с названия папки закладок?
Отредактировано Stkvsky (02-08-2021 12:32:51)
Отсутствует
Есть проблемка и по ходу оне не решатся..
Ну да. Скажем так: вряд ли такое описание поспособствует решению.
Тут нужен весь стиль, экстракт всех правил,
которые применяются к проблемным элементам,
чтобы накатить на нестилизованый профиль и увидеть проблему.
Вообще интересно, зачем по :hover задавать padding, в чём замысел?
чтобы названия для контейнера бралось с названия папки закладок
Тогда вроде как получается, что из папок журнала браться не должно,
но не стал их исключать, на всякий случай.
(async (sel, self) => ({ icon: "circle", colors: [ "#FF9800", "#03A9F4", "#FFC107", "#00BCD4", "#FFEB3B", "#009688", "#CDDC39", "#4CAF50", "#8BC34A", "#D32F2F", "#4949ff", "#C2185B", "#607D8B", "#7B1FA2", "#9E9E9E", "#673AB7", "#795548", "#3F51B5", "#FF5722", "#2196F3", ], initColors() { var colorName = "ucf-gen"; var css = "@-moz-document url(about:preferences#containers)," + " url-prefix(chrome://browser/content/browser.x) {\n"; this.colors.forEach((color, ind) => { var [ic, tc] = color.split(/\s*\|\s*/); css += `\t.identity-color-${colorName}${ind} {\n` + `\t\t--identity-tab-color: ${tc || ic};\n` + `\t\t--identity-icon-color: ${ic};\n\t}\n` }); var url = "data:text/css;charset=utf-8," + encodeURIComponent(css + "}"); var sss = Cc["@mozilla.org/content/style-sheet-service;1"] .getService(Ci.nsIStyleSheetService); sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET); var len = this.colors.length; var pref = "ucf.openInGeneratedContainer.lastColor"; var ind = Math.min(Services.prefs.getIntPref(pref, -1), len - 1); this.nextColor = () => { var next = ind + 1; Services.prefs.setIntPref(pref, ind = next == len ? 0 : next); return colorName + ind; } }, quit: false, init(topic) { Services.obs.addObserver(self = this, topic); var lt = "browser-lastwindow-close-granted"; var lw = () => this.quit = true; Services.obs.addObserver(lw, lt); Services.obs.addObserver(function quit(s, t) { self.quit = true; Services.obs.removeObserver(self, topic); Services.obs.removeObserver(lw, lt); Services.obs.removeObserver(quit, t); }, "quit-application-granted"); this.initColors(); this.newUsercontext = name => { var id = this.cis.create( name || `[ ${this.cis._lastUserContextId + 1} ]`, this.icon, this.nextColor() ).userContextId; this.saveGens(this.gens.add(id)); return id; } var cpref = "ucf.openInGeneratedContainer.containers"; var arr = Services.prefs.getStringPref(cpref, "").split(",").map(Number).filter(Boolean); if (arr.length) { var ids = this.cis.getPublicIdentities().map(i => i.userContextId); arr = arr.filter(id => ids.includes(id)); } this.gens = new Set(arr); (this.saveGens = () => Services.prefs.setStringPref(cpref, Array.from(this.gens).join(",")))(); }, observe(doc) { var list = doc.querySelectorAll(sel); if (!list.length) return; var menuitem = doc.createXULElement("menuitem"); for(var args of Object.entries({ selectiontype: "single", oncommand: "cmd(window)", nodetype: "folder|query", selection: "folder|query", label: "Открыть всё в контейнере", id: "placesContext_openContainer:tabs:newUsercontext" })) menuitem.setAttribute(...args); menuitem.cmd = this.cmd; menuitem.rnd = menuitem.constructor.prototype.render; menuitem.render = this.render; var [m1, m2] = menuitem.list = Array.from(list); (m2 || m1).after(menuitem); if (doc.documentElement.getAttribute("windowtype") != "navigator:browser") return; for(var btn of [ doc.getElementById("tabs-newtab-button"), doc.getElementById("new-tab-button") || doc.ownerGlobal.gNavToolbox.palette.querySelector("#new-tab-button") ]) if (btn) btn.checkForMiddleClick = this.click; var win = doc.ownerGlobal; this.redefDoSearch(win, win.customElements.get("searchbar").prototype); win.gBrowser.tabContainer.addEventListener("TabClose", this.tabClose); win.addEventListener("unload", this.winUnload, {once: true}); this.quit = false; }, winUnload(e) { var win = e.target.ownerGlobal; win.removeEventListener("TabClose", self.tabClose); if (self.quit) return; var gb = win.gBrowser; if (gb) for(var tab of gb.tabs) self.tabClose(null, tab); }, closed: new Set(), cis: ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm") .ContextualIdentityService, tabClose(e, tab = e.target) { var id = +tab.getAttribute("usercontextid"); id && self.gens.has(id) && self.closed.add(id); self.closed.size == 1 && ChromeUtils.idleDispatch(self.maybeRemove); }, maybeRemove() { var ids = Array.from(self.closed); self.closed.clear(); for(var id of ids) self.maybeRemoveById(id); }, maybeRemoveById(id) { for(var win of CustomizableUI.windows) if (win.document.querySelector(`tab.tabbrowser-tab[usercontextid="${id}"]`)) return; this.saveGens(this.gens.delete(id)); this.cis.remove(id); }, redefDoSearch(win, proto) { var code = `(openTrustedLinkIn => [ {${proto.doSearch}}, openTrustedLinkIn ])( function otl(url, where, params) { if (where != "current") params.resolveOnNewTabCreated = br => gBrowser.moveTabTo( gBrowser.getTabForBrowser(br), Infinity ), params.userContextId = otl.newUsercontext( document.getElementById("searchbar").value ); openTrustedLinkIn(url, where, params); } );`; (this.redefDoSearch = (win, proto) => { var [obj, func] = win.eval(code); Object.assign(proto, obj); func.newUsercontext = this.newUsercontext; })(win, proto); }, click(btn, e) { if (!(e.button != 2 || e.ctrlKey || e.shiftKey)) { var txt = e.view.readFromClipboard(); if (txt) { var urls = txt.split("\n").map(self.map).filter(Boolean); if (urls.length) return e.preventDefault(), self.openFromClipboard(e.view, urls); } } e.view.checkForMiddleClick(btn, e); }, eo: Object.create(null), map(str) { str = str.trim(); try { var scheme = Services.io.extractScheme(str); var ph = Services.io.getProtocolHandler(scheme); if (ph.scheme == scheme) return Services.io.newURI(str) && {uri: str}; } catch {} }, openFromClipboard(win, urls) { if (win.OpenInTabsUtils.confirmOpenInTabs(urls.length, win)) urls.load = true, this.open(win, this.eo, urls); }, async render() { this.rnd(); await new Promise(this.ownerGlobal.requestAnimationFrame); this.hidden || (this.hidden = this.list.every(self.every)); }, every: node => node.disabled || node.hidden, cmd(win) { var {_view, triggerNode} = this.parentNode; var node = triggerNode._placesView && triggerNode._placesView.result.root; self.open(win, node || _view.selectedNode || _view.result.root); }, open(win, node, list) { var gbw = Cu.import("resource:///modules/PlacesUIUtils.jsm", {}).getBrowserWindow; var w = gbw(win); this.pu = w.PlacesUIUtils; this.fs = w.PlacesUtils.favicons; this.sysp = w.E10SUtils.SERIALIZED_SYSTEMPRINCIPAL; (this.open = (win, node, list) => { var {bookmarkGuid: guid, title} = node; if (guid && title) title = win.PlacesUtils.bookmarks.getLocalizedTitle({guid, title}); this.openURLs(gbw(win), list || win.PlacesUtils.getURLsForContainerNode(node), title); guid && this.pu.doCommand(win, "placesCmd_delete"); })(win, node, list); }, async openURLs(win, urls, title) { var userContextId = this.newUsercontext(title); var mark = !win.PrivateBrowsingUtils.isWindowPrivate(win); var {load} = urls, gb = win.gBrowser, pos = gb.selectedTab._tPos; for(var {uri, title, isBookmark} of urls) try { if (mark) isBookmark ? this.pu.markPageAsFollowedBookmark(uri) : this.pu.markPageAsTyped(uri); if (load) { gb.addTrustedTab(uri, {index: ++pos, userContextId}); continue; } var state = {userContextId, entries: [{ url: uri, title: title || uri, triggeringPrincipal_base64: this.sysp }]}; var [,, data, mime] = await new Promise( resolve => this.fs.getFaviconDataForPage( Services.io.newURI(uri), (...args) => resolve(args), 16 ) ); if (data.length) state.image = `data:${ mime || "image/x-icon" };base64,${ btoa(String.fromCharCode(...data)) }`; var tab = gb.addTrustedTab(null, {index: ++pos, userContextId}); win.SessionStore.setTabState(tab, state); } catch {}; } }).init("chrome-document-loaded"))( "#placesContext_openBookmarkContainer\\:tabs,#placesContext_openContainer\\:tabs" );
Отсутствует
Dumby
Класс, спасибо большое
Добавлено 02-08-2021 22:53:30
Dumby
Еще можете пожалуйста изменить процесс добавления контейнера в папку закладок так же?
Вот ваш код для custom buttons который добавляет в меню вкладки (ПКМ по вкладке - Добавить контейнер в закладки)
// Добавить контейнер в закладки.................... (async id => { var menuitem = document.createXULElement("menuitem"); document.getElementById(id).after(menuitem); typeof addDestructor == "function" && addDestructor(() => menuitem.remove()); menuitem.render = function() { this.id = "context_bookmarkContainer"; this.label = "Добавить контейнер в закладки"; this.setAttribute("oncommand", "bookmark()"); var bm = PlacesUtils.bookmarks, attr = "usercontextid"; var {toolbarGuid: parentGuid, TYPE_FOLDER: type} = bm; this.bookmark = async () => { var tab = TabContextMenu.contextTab; var title = tab.label, id = tab.getAttribute(attr); var {guid} = await bm.insert({title, parentGuid, type}); var tabs = []; for(tab of gBrowser.visibleTabs) tab.getAttribute(attr) == id && tabs.unshift(tab) && await bm.insert({ parentGuid: guid, title: tab.label, url: tab.linkedBrowser.currentURI.spec }); gBrowser.removeTabs(tabs); } var raf = () => menuitem.hidden = !TabContextMenu.contextTab.hasAttribute(attr); var {render} = this.constructor.prototype; (this.render = () => { requestAnimationFrame(raf); render.call(menuitem); })(); } })("context_reopenInContainer");
Отредактировано Stkvsky (03-08-2021 01:39:06)
Отсутствует
Stkvsky
/* var title = tab.label, id = tab.getAttribute(attr); */ var id = tab.getAttribute(attr); var title = ContextualIdentityService.getUserContextLabel(id);
Отсутствует
Dumby
А можно вас еще попросить, вот когда нажимаешь "звездочку" добавить в закладки, которая в адресной строке
Открывается меню добавления в закладки и там есть миниатюра эскиз изображения страницы
Отредактировано Stkvsky (04-08-2021 14:10:25)
Отсутствует
чтобы при наведении курсора поверх вкладки это изображение эскиза страницы показывалось под вкладкой
Именно это неохота, а какое-нибудь можно попробовать.
(async query => { var width = 300; var tid, once = {once: true}; var nh = tab => !tab.matches(":hover"); await delayedStartupPromise; var slot = gBrowser.selectedTab.flattenedTreeParentNode; var lst = e => { var tab = e.target; if ( tab.nodeName == "tab" //&& !tab.selected && tab.linkedPanel ) tid && clearTimeout(tid), tid = setTimeout(onTab, 200, tab); } var args = ["mouseenter", lst, true], cb; if (addEventListener != window.addEventListener) cb = true, addEventListener(...args, slot); else slot.addEventListener(...args), addEventListener("unload", () => slot.removeEventListener(...args) , once); var onTab = tab => { tid = 0; if (nh(tab)) return; var br = tab.linkedBrowser; var cwg = br.browsingContext?.currentWindowGlobal; cwg && showPanel(tab, br, cwg); } var showPanel = (...args) => { var popup = (cb ? this : mainPopupSet) .appendChild(document.createXULElement("panel")); popup.setAttribute("noautofocus", true); popup.setAttribute("consumeoutsideclicks", "never"); var hide = () => popup.hidePopup(); var canvas = popup.appendChild(document.createElement("canvas")); canvas.width = width; var context = canvas.getContext("2d", {alpha: false}); if (parseInt(Services.appinfo.platformVersion) >= 81) { var wp = "width", hp = "height"; query += "Info"; } else { var wp = 0, hp = 1; query += "Size"; } (showPanel = async (tab, br, cwg) => { var res = await cwg.getActor("Thumbnails").sendQuery(query); if (nh(tab)) return; var w = res[wp]; if (w < width) return; var h = res[hp], k = width / w; try {var bitmap = await cwg.drawSnapshot( new DOMRect(0, 0, w, h), k, "white" );} catch {return;} if (nh(tab) || !bitmap) return; canvas.height = k * h; context.drawImage(bitmap, 0, 0); bitmap.close(); popup.openPopup(tab, "after_start"); tab.addEventListener("mouseleave", hide, once); })(...args); } })("Browser:Thumbnail:Content");
Отсутствует
Dumby
Еще такой вопрос
Сейчас если вкладка не загружена, например после перезапуска браузера, то миниатюра страницы не отображается
А можно ли сделать чтобы эти миниатюры куда нибудь сохранялись и отображались вне зависимости от того загружена вкладка или нет?
Отредактировано Stkvsky (07-08-2021 20:25:48)
Отсутствует
Именно это неохота, а какое-нибудь можно попробовать.
А для TST такое же можете сделать?
GitHub - AMO
Добавлено 07-08-2021 21:12:29
Stkvsky
Расширение для [firefox]52 просто тупо загружало вкладку и настроить это было нельзя, поэтому выкинул его еще тогда.
Отредактировано _zt (07-08-2021 21:12:44)
Отсутствует
сделать чтобы эти миниатюры куда нибудь сохранялись
Эти, это которые уже срисовывались по наведению? Ну да, какие же ещё «эти».
Самое простое, записывать в .json (при выходе). Жуть, конечно.
(async query => { var width = 300; var tid, once = {once: true}; var nh = tab => !tab.matches(":hover"); await delayedStartupPromise; var slot = gBrowser.selectedTab.flattenedTreeParentNode; var lst = e => { var tab = e.target; if ( tab.nodeName == "tab" //&& !tab.selected ) tid && clearTimeout(tid), tid = setTimeout(onTab, 200, tab); } var args = ["mouseenter", lst, true], cb = false; if (addEventListener != window.addEventListener) cb = true, addEventListener(...args, slot); else slot.addEventListener(...args), addEventListener("unload", () => slot.removeEventListener(...args) , once); var g = Cu.getGlobalForObject(Cu); var key = "TabHoverSnapshotsStorage"; var storage = g[key]; if (!storage) { var func = (key, cb) => (this[key] = {get data() { var mo = p => ChromeUtils.import(`resource://gre/modules/${p}.jsm`)[p]; var file = mo("Services").dirsvc.get("ProfD", Ci.nsIFile); file.append(key + ".json"); var data = Object.create(null); try {Object.assign(data, JSON.parse(Cu.readUTF8File(file)));} catch {} var skip = true; var blocker = () => { if (skip) return Promise.resolve(); var fu = mo("FileUtils"); var sfs = fu.openSafeFileOutputStream(file); var sis = Cc["@mozilla.org/io/string-input-stream;1"] .createInstance(Ci.nsISupportsCString); sis.data = JSON.stringify(data, null, "\t"); return new Promise(resolve => mo("NetUtil").asyncCopy( sis, sfs, () => resolve(fu.closeSafeFileOutputStream(sfs)) )); } var pbc = mo("AsyncShutdown").profileBeforeChange; pbc.addBlocker(key, blocker); if (cb) this.destroy = save => { delete globalThis[key]; pbc.removeBlocker(blocker); save && blocker(); } this.images = Object.create(null); this.set = (...args) => { skip = false; (this.set = (url, src) => data[url] = src)(...args); } delete this.data; return this.data = data; }}); storage = g.eval(`(${func})("${key}", ${cb});`); } cb && addDestructor(r => r[5] == "e" && g[key]?.destroy(r[0] == "u")); var onTab = tab => { tid = 0; if (nh(tab)) return; var br = tab.linkedBrowser; var cwg = br.browsingContext?.currentWindowGlobal; openPopup(tab, br, cwg); } var openPopup = (...args) => { var popup = (cb ? this : mainPopupSet) .appendChild(document.createXULElement("menupopup")); popup.setAttribute("ignorekeys", true); popup.setAttribute("consumeoutsideclicks", "never"); popup.shadowRoot.querySelector("style").append(` :host { padding: 0 !important; margin-top: 1px !important; -moz-appearance: none !important; } arrowscrollbox::part(scrollbutton-up), arrowscrollbox::part(scrollbutton-down) { display: none !important; } `); var hide = () => popup.hidePopup(); var canvas = popup.appendChild(document.createElement("canvas")); canvas.width = width; var context = canvas.getContext("2d", {alpha: false}); if (parseInt(Services.appinfo.platformVersion) >= 81) { var wp = "width", hp = "height"; query += "Info"; } else { var wp = 0, hp = 1; query += "Size"; } (openPopup = async (tab, br, cwg) => { var url = br.currentURI.specIgnoringRef; var store = url.length < 640; if (tab.linkedPanel) { if (!cwg) return; var res = await cwg.getActor("Thumbnails").sendQuery(query); if (nh(tab)) return; var w = res[wp]; if (w < width) return; var h = res[hp], k = width / w; try {var bitmap = await cwg.drawSnapshot( new DOMRect(0, 0, w, h), k, "white" );} catch {} if (nh(tab) || !bitmap) return; canvas.height = k * h; context.drawImage(bitmap, 0, 0); bitmap.close(); if (store) { var src = canvas.toDataURL(); if (src != storage.data[url]) storage.set(url, src), delete storage.images[url]; } } else { if (!store) return; var img = storage.images?.[url]; if (!img) { var src = storage.data[url]; if (!src) return; img = storage.images[url] = new Image(); img.src = src; await new Promise(resolve => img.onloadend = resolve); if (nh(tab)) return; } context.drawImage(img, 0, 0, width, canvas.height = img.naturalHeight * width / img.naturalWidth ); } popup.openPopup(tab, "after_start"); tab.addEventListener("mouseleave", hide, once); })(...args); } })("Browser:Thumbnail:Content");
А для TST такое же можете сделать?
Попробовал JSM'кой, вроде чего-то показывает.
Импортировать из custom_script.js, примерно так:
(async url => ChromeUtils.import(url))(
"chrome://user_chrome_files/content/custom_scripts/TreeStyleTabPreviewPopup.jsm"
);
var EXPORTED_SYMBOLS = ["TreeStyleTabPreviewPopupChild", "TreeStyleTabPreviewPopupParent"]; if (!ChromeUtils.domProcessChild.childID) { var popupWidth = 300; var name = "TreeStyleTabPreviewPopup"; var host, tstId = "treestyletab@piro.sakura.ne.jp"; var manager = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm") .ExtensionParent.apiManager; var tt = manager.global.tabTracker; var wait = (e, isAppShutdown) => isAppShutdown || manager.on("ready", onReady); var onReady = (e, addon) => { if (addon.id != tstId) return; manager.off("ready", onReady); addon.once("shutdown", wait); init(addon.uuid); } wait(); var init = uuid => { if (host == uuid) return; host && ChromeUtils.unregisterWindowActor(name); ChromeUtils.registerWindowActor(name, { parent: {moduleURI: __URI__}, messageManagerGroups: ["webext-browsers"], child: {moduleURI: __URI__, events: {mouseover: {}}}, matches: [`moz-extension://${host = uuid}/sidebar/sidebar.html*`] }); } var popupId = "ucf-tst-preview-popup"; var TreeStyleTabPreviewPopupParent = class extends JSWindowActorParent { actorCreated() { var doc = this.browsingContext.topChromeWindow.document; var popup = doc.getElementById(popupId); if (!popup) { popup = doc.createXULElement("menupopup"); popup.id = popupId; popup.setAttribute("ignorekeys", true); popup.setAttribute("consumeoutsideclicks", "never"); popup.shadowRoot.querySelector("style").append(` :host { padding: 0 !important; -moz-appearance: none !important; } arrowscrollbox::part(scrollbutton-up), arrowscrollbox::part(scrollbutton-down) { display: none !important; } `); (popup.canvas = popup.appendChild(doc.createElement("canvas"))) .width = popupWidth; popup.context = popup.canvas.getContext("2d", {alpha: false}); doc.getElementById("mainPopupSet").append(popup); } this.popup = popup; } receiveMessage(msg) { var id = msg.data; if (!id) return this.popup.hidePopup(); var tab = tt.getTab(+id.slice(4)); if (tab/* && !tab.selected*/) { var cwg = tab.linkedBrowser.browsingContext?.currentWindowGlobal; cwg && this.drawSnapshot(tab.ownerDocument, cwg, id); } } async drawSnapshot(doc, cwg, id) { var {width, height} = await cwg.getActor("Thumbnails") .sendQuery("Browser:Thumbnail:ContentInfo"); if (width < 200) return; var k = popupWidth / width; try {var bitmap = await cwg.drawSnapshot( new DOMRect(0, 0, width, height), k, "white" );} catch {} if (bitmap) { var data = await this.sendQuery(id); if (data) { this.popup.canvas.height = k * height; this.popup.context.drawImage(bitmap, 0, 0); bitmap.close(); this.popup.openPopupAtScreenRect("after_start", ...data); } } } didDestroy() { this.popup = null; } } } class TreeStyleTabPreviewPopupChild extends JSWindowActorChild { actorCreated() { this.args = ["mouseleave", () => { this.tab = null; this.tid || this.sendAsyncMessage(""); this.tid = this.clearTimeout(); }, {once: true}]; } mult(val) { return this * val; } receiveMessage(msg) { var tab = this.document.getElementById(msg.name); var res = tab?.matches(":hover"); if (res) { var {x, y, width, height} = tab.getBoundingClientRect(); var win = tab.ownerGlobal; res = [ x + win.mozInnerScreenX, y + win.mozInnerScreenY, width, height ]; var z = win.windowUtils.screenPixelsPerCSSPixel; if (z != 1) res = res.map(this.mult, z); } return res; } handleEvent(e) { var tab = e.target.closest("tab-item"); if (!tab || tab == this.tab) return; this.clearTimeout(); this.tid = this.contentWindow .setTimeout(this.onTab, 200, this.tab = tab, this); tab.addEventListener(...this.args); } clearTimeout() { this.tid && this.contentWindow.clearTimeout(this.tid); } onTab(tab, self) { self.tid = null; tab.wrappedJSObject.apiTab.discarded || self.sendAsyncMessage("", tab.id); } didDestroy() { this.tab = null; } }
Custom Buttons 0.0.7.0.0.19, paxmod и bootstrap в zip-папке.
Плюс, добавлен Custom Buttons 91.0 для Thunderbird 91.0
(только для TB, и только для TB 91).
Отсутствует
Эти, это которые уже срисовывались по наведению? Ну да, какие же ещё «эти».
Самое простое, записывать в .json (при выходе). Жуть, конечно.
Да эти, извиняюсь за французский) работает, класс, благодарю
Отредактировано Stkvsky (09-08-2021 20:11:13)
Отсутствует
Dumby
Шикарно, спасибо.
Можете, пожалуйста, добавить:
1. Тайм-аут появления миниатюры.
2. Авто-расположение -
слева от tab-item, если панель справа,
справа от tab-item, если панель слева.
С выравниванием верхнего края миниатюры по верхнему краю tab-item.
3. Кнопки включения отключения скрипта, примерно как это сделано здесь.
Отсутствует
Попробовал JSM'кой, вроде чего-то показывает.
Ох какая годнота, спасибо Dumby !
А можно попросить для TST сделать что-то подобное, чтобы отображалось "host | title", без переписывания тайтла страниц?
https://forum.mozilla-russia.org/viewtopic.php?pid=788775#p788775
начало тут
https://forum.mozilla-russia.org/viewtopic.php?pid=788701#p788701
Отсутствует
Dumby
ВЫ бы не могли пожалуйста подправить, сейчас когда отображается миниатюра вкладки при наведении - не работает скролл колесом
(async query => { var width = 300; var tid, once = {once: true}; var nh = tab => !tab.matches(":hover"); await delayedStartupPromise; var slot = gBrowser.selectedTab.flattenedTreeParentNode; var lst = e => { var tab = e.target; if ( tab.nodeName == "tab" //&& !tab.selected ) tid && clearTimeout(tid), tid = setTimeout(onTab, 200, tab); } var args = ["mouseenter", lst, true], cb = false; if (addEventListener != window.addEventListener) cb = true, addEventListener(...args, slot); else slot.addEventListener(...args), addEventListener("unload", () => slot.removeEventListener(...args) , once); var g = Cu.getGlobalForObject(Cu); var key = "TabHoverSnapshotsStorage"; var storage = g[key]; if (!storage) { var func = (key, cb) => (this[key] = {get data() { var mo = p => ChromeUtils.import(`resource://gre/modules/${p}.jsm`)[p]; var file = mo("Services").dirsvc.get("ProfD", Ci.nsIFile); file.append(key + ".json"); var data = Object.create(null); try {Object.assign(data, JSON.parse(Cu.readUTF8File(file)));} catch {} var skip = true; var blocker = () => { if (skip) return Promise.resolve(); var fu = mo("FileUtils"); var sfs = fu.openSafeFileOutputStream(file); var sis = Cc["@mozilla.org/io/string-input-stream;1"] .createInstance(Ci.nsISupportsCString); sis.data = JSON.stringify(data, null, "\t"); return new Promise(resolve => mo("NetUtil").asyncCopy( sis, sfs, () => resolve(fu.closeSafeFileOutputStream(sfs)) )); } var pbc = mo("AsyncShutdown").profileBeforeChange; pbc.addBlocker(key, blocker); if (cb) this.destroy = save => { delete globalThis[key]; pbc.removeBlocker(blocker); save && blocker(); } this.images = Object.create(null); this.set = (...args) => { skip = false; (this.set = (url, src) => data[url] = src)(...args); } delete this.data; return this.data = data; }}); storage = g.eval(`(${func})("${key}", ${cb});`); } cb && addDestructor(r => r[5] == "e" && g[key]?.destroy(r[0] == "u")); var onTab = tab => { tid = 0; if (nh(tab)) return; var br = tab.linkedBrowser; var cwg = br.browsingContext?.currentWindowGlobal; openPopup(tab, br, cwg); } var openPopup = (...args) => { var popup = (cb ? this : mainPopupSet) .appendChild(document.createXULElement("menupopup")); popup.setAttribute("ignorekeys", true); popup.setAttribute("consumeoutsideclicks", "never"); popup.shadowRoot.querySelector("style").append(` :host { padding: 0 !important; margin-top: 1px !important; -moz-appearance: none !important; } arrowscrollbox::part(scrollbutton-up), arrowscrollbox::part(scrollbutton-down) { display: none !important; } `); var hide = () => popup.hidePopup(); var canvas = popup.appendChild(document.createElement("canvas")); canvas.width = width; var context = canvas.getContext("2d", {alpha: false}); if (parseInt(Services.appinfo.platformVersion) >= 81) { var wp = "width", hp = "height"; query += "Info"; } else { var wp = 0, hp = 1; query += "Size"; } (openPopup = async (tab, br, cwg) => { var url = br.currentURI.specIgnoringRef; var store = url.length < 640; if (tab.linkedPanel) { if (!cwg) return; var res = await cwg.getActor("Thumbnails").sendQuery(query); if (nh(tab)) return; var w = res[wp]; if (w < width) return; var h = res[hp], k = width / w; try {var bitmap = await cwg.drawSnapshot( new DOMRect(0, 0, w, h), k, "white" );} catch {} if (nh(tab) || !bitmap) return; canvas.height = k * h; context.drawImage(bitmap, 0, 0); bitmap.close(); if (store) { var src = canvas.toDataURL(); if (src != storage.data[url]) storage.set(url, src), delete storage.images[url]; } } else { if (!store) return; var img = storage.images?.[url]; if (!img) { var src = storage.data[url]; if (!src) return; img = storage.images[url] = new Image(); img.src = src; await new Promise(resolve => img.onloadend = resolve); if (nh(tab)) return; } context.drawImage(img, 0, 0, width, canvas.height = img.naturalHeight * width / img.naturalWidth ); } popup.openPopup(tab, "after_start"); tab.addEventListener("mouseleave", hide, once); })(...args); } })("Browser:Thumbnail:Content");
Отредактировано Stkvsky (10-08-2021 14:04:17)
Отсутствует
Можете, пожалуйста, добавить
Попробую
var timeout = 500; if (!ChromeUtils.domProcessChild.childID) { var popupWidth = 300; var label = "Some Label"; var tooltiptext = "Some Tooltip Text"; var imgEnabled = "chrome://browser/skin/preferences/face-smile.svg"; var imgDisabled = "chrome://browser/skin/preferences/face-sad.svg"; var btnImage, popupPosition, enabled, addonUUID, registeredUUID; var mo = (p, r = "gre") => ChromeUtils.import(`resource://${r}/modules/${p}.jsm`)[p]; //-------[ Addon ]------------------------------------------------------ var addonId = "treestyletab@piro.sakura.ne.jp"; var manager = mo("ExtensionParent").apiManager; var tt = manager.global.tabTracker; var waitAddon = (e, isAppShutdown) => isAppShutdown || ( addonUUID = null, manager.on("ready", onReady) ); var onReady = (e, addon) => { if (addon.id != addonId) return; manager.off("ready", onReady); addon.once("shutdown", waitAddon); addonUUID = addon.uuid; checkRegistration(); } waitAddon(); //-------[ Actor registration ]------------------------------------------------------ var name = "TreeStyleTabPreviewPopup"; var reg = () => ChromeUtils.registerWindowActor(name, { parent: {moduleURI: __URI__}, messageManagerGroups: ["webext-browsers"], child: {moduleURI: __URI__, events: {mouseover: {}}}, matches: [`moz-extension://${registeredUUID = addonUUID}/sidebar/sidebar.html*`] }); var unreg = () => { registeredUUID = null; ChromeUtils.unregisterWindowActor(name); } var checkRegistration = () => { if (enabled) { if (registeredUUID) { if (registeredUUID == addonUUID) return; addonUUID && unreg(); } addonUUID && reg(); } else if (registeredUUID && addonUUID) unreg(); } //------[ Observer ]------------------------------------------------------ var {prefs, obs} = mo("Services"); var pref = "ucf_tst_preview_popup"; var branch = prefs.getBranch("sidebar."); var prefObs = { observe(b, t, data) { this[data]?.(branch.getBoolPref(data, true)); }, position_start: val => popupPosition = val ? "end_before" : "start_before", [pref](val) { btnImage = (enabled = val) ? imgEnabled : imgDisabled; this.setBtnsImg(); checkRegistration(); }, setBtnsImg: () => prefObs.setBtnsImg = () => { var widget = cui.getWidget(btnId); for(var win of cui.windows) widget.forWindow(win).node?.setAttribute("image", btnImage); } }; for (let p of [pref, "position_start"]) prefObs.observe(null, null, p); branch.addObserver("", prefObs); obs.addObserver(function quit(s, topic) { obs.removeObserver(quit, topic); branch.removeObserver("", prefObs); }, "quit-application-granted"); //-------[ Widget ]------------------------------------------------------ var popupId = "ucf-tst-preview-popup"; var btnId = popupId + "-button"; var cui = mo("CustomizableUI", ""); var toggle = () => branch.setBoolPref(pref, !enabled); cui.createWidget({ id: btnId, label, tooltiptext, localized: false, onCreated(btn) { btn.render = this.render; btn._handleClick = toggle; btn.setAttribute("image", btnImage); }, render() { delete this.render; this.render(); this.firstChild.style.setProperty("min-height", "16px", "important"); } }); //-------[ Actor ]------------------------------------------------------ var TreeStyleTabPreviewPopupParent = class extends JSWindowActorParent { actorCreated() { var doc = this.browsingContext.topChromeWindow.document; var popup = doc.getElementById(popupId); if (!popup) { popup = doc.createXULElement("menupopup"); popup.id = popupId; popup.setAttribute("ignorekeys", true); popup.setAttribute("rolluponmousewheel", true); popup.setAttribute("consumeoutsideclicks", "never"); popup.shadowRoot.querySelector("style").append(` :host { padding: 0 !important; -moz-appearance: none !important; } arrowscrollbox::part(scrollbutton-up), arrowscrollbox::part(scrollbutton-down) { display: none !important; } `); (popup.canvas = popup.appendChild(doc.createElement("canvas"))) .width = popupWidth; popup.context = popup.canvas.getContext("2d", {alpha: false}); doc.getElementById("mainPopupSet").append(popup); } this.popup = popup; } receiveMessage(msg) { var id = msg.data; if (!id) return this.popup.hidePopup(); var tab = tt.getTab(+id.slice(4)); if (tab/* && !tab.selected*/) { var cwg = tab.linkedBrowser.browsingContext?.currentWindowGlobal; cwg && this.drawSnapshot(tab.ownerDocument, cwg, id); } } async drawSnapshot(doc, cwg, id) { var {width, height} = await cwg.getActor("Thumbnails") .sendQuery("Browser:Thumbnail:ContentInfo"); if (width < 200) return; var k = popupWidth / width; try {var bitmap = await cwg.drawSnapshot( new DOMRect(0, 0, width, height), k, "white" );} catch {} if (bitmap) { var data = await this.sendQuery(id); if (data) { this.popup.canvas.height = k * height; this.popup.context.drawImage(bitmap, 0, 0); bitmap.close(); this.popup.openPopupAtScreenRect(popupPosition, ...data); } } } didDestroy() { this.popup.hidePopup(); this.popup = null; } } } class TreeStyleTabPreviewPopupChild extends JSWindowActorChild { actorCreated() { this.args = ["mouseleave", () => { this.tab = null; this.tid || this.sendAsyncMessage(""); this.tid = this.clearTimeout(); }, {once: true}]; } mult(val) { return this * val; } receiveMessage(msg) { var tab = this.document.getElementById(msg.name); var res = tab?.matches(":hover"); if (res) { var {x, y, width, height} = tab.getBoundingClientRect(); var win = tab.ownerGlobal; res = [ x + win.mozInnerScreenX, y + win.mozInnerScreenY, width, height ]; var z = win.windowUtils.screenPixelsPerCSSPixel; if (z != 1) res = res.map(this.mult, z); } return res; } handleEvent(e) { var tab = e.target.closest("tab-item"); if (!tab || tab == this.tab) return; this.clearTimeout(); this.tid = this.contentWindow .setTimeout(this.onTab, timeout, this.tab = tab, this); tab.addEventListener(...this.args); } clearTimeout() { this.tid && this.contentWindow.clearTimeout(this.tid); } onTab(tab, self) { self.tid = null; tab.wrappedJSObject.apiTab.discarded || self.sendAsyncMessage("", tab.id); } didDestroy() { this.tab = null; } } var EXPORTED_SYMBOLS = ["TreeStyleTabPreviewPopupChild", "TreeStyleTabPreviewPopupParent"];
чтобы отображалось "host | title", без переписывания тайтла страниц
Дописал некое вмешательство в местную кустомэлементщину.
Ненадёжно это, наверно.
var EXPORTED_SYMBOLS = ["TreeStyleTabPreviewPopupChild", "TreeStyleTabPreviewPopupParent"]; if (!ChromeUtils.domProcessChild.childID) { var popupWidth = 300; var name = "TreeStyleTabPreviewPopup"; var host, tstId = "treestyletab@piro.sakura.ne.jp"; var manager = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm") .ExtensionParent.apiManager; var tt = manager.global.tabTracker; var wait = (e, isAppShutdown) => isAppShutdown || manager.on("ready", onReady); var onReady = (e, addon) => { if (addon.id != tstId) return; manager.off("ready", onReady); addon.once("shutdown", wait); init(addon.uuid); } wait(); var init = uuid => { if (host == uuid) return; host && ChromeUtils.unregisterWindowActor(name); ChromeUtils.registerWindowActor(name, { parent: {moduleURI: __URI__}, messageManagerGroups: ["webext-browsers"], child: {moduleURI: __URI__, events: {DOMDocElementInserted: {}, mouseover: {}}}, matches: [`moz-extension://${host = uuid}/sidebar/sidebar.html*`] }); } var popupId = "ucf-tst-preview-popup"; var TreeStyleTabPreviewPopupParent = class extends JSWindowActorParent { actorCreated() { var doc = this.browsingContext.topChromeWindow.document; var popup = doc.getElementById(popupId); if (!popup) { popup = doc.createXULElement("menupopup"); popup.id = popupId; popup.setAttribute("ignorekeys", true); popup.setAttribute("rolluponmousewheel", true); popup.setAttribute("consumeoutsideclicks", "never"); popup.shadowRoot.querySelector("style").append(` :host { padding: 0 !important; -moz-appearance: none !important; } arrowscrollbox::part(scrollbutton-up), arrowscrollbox::part(scrollbutton-down) { display: none !important; } `); (popup.canvas = popup.appendChild(doc.createElement("canvas"))) .width = popupWidth; popup.context = popup.canvas.getContext("2d", {alpha: false}); doc.getElementById("mainPopupSet").append(popup); } this.popup = popup; } receiveMessage(msg) { var id = msg.data; if (!id) return this.popup.hidePopup(); var tab = tt.getTab(+id.slice(4)); if (tab/* && !tab.selected*/) { var cwg = tab.linkedBrowser.browsingContext?.currentWindowGlobal; cwg && this.drawSnapshot(tab.ownerDocument, cwg, id); } } async drawSnapshot(doc, cwg, id) { var {width, height} = await cwg.getActor("Thumbnails") .sendQuery("Browser:Thumbnail:ContentInfo"); if (width < 200) return; var k = popupWidth / width; try {var bitmap = await cwg.drawSnapshot( new DOMRect(0, 0, width, height), k, "white" );} catch {} if (bitmap) { var data = await this.sendQuery(id); if (data) { this.popup.canvas.height = k * height; this.popup.context.drawImage(bitmap, 0, 0); bitmap.close(); this.popup.openPopupAtScreenRect("after_start", ...data); } } } didDestroy() { this.popup.hidePopup(); this.popup = null; } } } function updateTextContent() { var span = this.firstElementChild; if (!span) return; var val = this.getAttribute("value"); var url = this.parentNode.dataset.currentUri; if (url?.startsWith("http")) try { var {hostname} = new URL(url); if (hostname) val = `${hostname} | ${val}`; } catch {} span.textContent = val || ""; } class TreeStyleTabPreviewPopupChild extends JSWindowActorChild { actorCreated() { this.args = ["mouseleave", () => { this.tab = null; this.tid || this.sendAsyncMessage(""); this.tid = this.clearTimeout(); }, {once: true}]; } mult(val) { return this * val; } receiveMessage(msg) { var tab = this.document.getElementById(msg.name); var res = tab?.matches(":hover"); if (res) { var {x, y, width, height} = tab.getBoundingClientRect(); var win = tab.ownerGlobal; res = [ x + win.mozInnerScreenX, y + win.mozInnerScreenY, width, height ]; var z = win.windowUtils.screenPixelsPerCSSPixel; if (z != 1) res = res.map(this.mult, z); } return res; } labDefined(lab) { lab.wrappedJSObject.prototype .updateTextContent = updateTextContent; } handleEvent(e) { e.target.ownerGlobal.customElements .whenDefined("tab-label").then(this.labDefined); this.handleEvent = this.mouseover; } mouseover(e) { var tab = e.target.closest("tab-item"); if (!tab || tab == this.tab) return; this.clearTimeout(); this.tid = this.contentWindow .setTimeout(this.onTab, 200, this.tab = tab, this); tab.addEventListener(...this.args); } clearTimeout() { this.tid && this.contentWindow.clearTimeout(this.tid); } onTab(tab, self) { self.tid = null; tab.wrappedJSObject.apiTab.discarded || self.sendAsyncMessage("", tab.id); } didDestroy() { this.tab = null; } }
когда отображается миниатюра вкладки при наведении - не работает скролл колесом
Да, вижу. Вроде помогает, если
после
popup.setAttribute("ignorekeys", true);
добавить
popup.setAttribute("rolluponmousewheel", true);
Отсутствует