Skip to content

Commit 73716d2

Browse files
committed
new script: add tabThumbnailTooltip.uc.js
1 parent a7387d3 commit 73716d2

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

JS/tabThumbnailTooltip.uc.js

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// ==UserScript==
2+
// @name Tab Thumbnail Tooltip
3+
// @version 1.0
4+
// @author aminomancer
5+
// @homepage https://github.com/aminomancer/uc.css.js
6+
// @description Show a large thumbnail image to preview tab content when hovering a tab.
7+
// @license This Source Code Form is subject to the terms of the Creative Commons Attribution-NonCommercial-ShareAlike International License, v. 4.0. If a copy of the CC BY-NC-SA 4.0 was not distributed with this file, You can obtain one at http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
8+
// ==/UserScript==
9+
10+
class TabThumbnail {
11+
static config = {
12+
"Preview width": 250, // Thumbnail width, in pixels (can override with CSS too)
13+
"Preview height": 180, // Thumbnail height
14+
"Update interval": 30, // How often to refresh the thumbnail, in milliseconds
15+
};
16+
get tooltip() {
17+
return this._tooltip || (this._tooltip = document.getElementById("tabThumbTooltip"));
18+
}
19+
get tabLabel() {
20+
return this._tabLabel || (this._tabLabel = this.tooltip.querySelector("#tabThumbLabel"));
21+
}
22+
get thumbBox() {
23+
return this._thumbBox || (this._thumbBox = this.tooltip.querySelector("#tabThumbBox"));
24+
}
25+
constructor() {
26+
this._updateTimer = null;
27+
this.config = TabThumbnail.config;
28+
const markup =
29+
/* html */
30+
`<tooltip
31+
id="tabThumbTooltip"
32+
noautohide="true"
33+
orient="vertical"
34+
onpopupshowing="ucTabThumbnail.onPopupShowing();"
35+
onpopuphiding="ucTabThumbnail.onPopupHiding();"
36+
hide-thumbnail="true"
37+
style="visibility: collapse;">
38+
<vbox id ="tabThumbBox" flex="true">
39+
<description id="tabThumbLabel" class="tooltip-label" flex="true"/>
40+
<toolbarseparator />
41+
<hbox id="tabCanvasBox" flex="true">
42+
<html:div id="tabThumbCanvas"></html:div>
43+
</hbox>
44+
</vbox>
45+
</tooltip>`;
46+
this.registerSheet();
47+
document
48+
.getElementById("mainPopupSet")
49+
.appendChild(MozXULElement.parseXULToFragment(markup));
50+
gBrowser.tabContainer.tooltip = "tabThumbTooltip";
51+
addEventListener("unload", () => this.cancelTimer(), false);
52+
}
53+
async onPopupShowing() {
54+
let ready = await this.showPreview();
55+
if (ready) {
56+
this.tooltip.style.removeProperty("visibility");
57+
}
58+
return ready;
59+
}
60+
onPopupHiding() {
61+
this.tooltip.style.setProperty("visibility", "collapse");
62+
this.cancelTimer();
63+
}
64+
async showPreview(update) {
65+
let tab = this.tooltip.triggerNode?.closest(".tabbrowser-tab");
66+
if (!tab) return false;
67+
if (!update) {
68+
this.tooltip.setAttribute("position", "after_start");
69+
this.tooltip.moveToAnchor(tab, "after_start");
70+
}
71+
const { config } = this;
72+
this.tabLabel.textContent = gBrowser.getTabTooltip(tab);
73+
let canvas = PageThumbs.createCanvas(window);
74+
let browser = tab.linkedBrowser;
75+
let pending = tab.hasAttribute("pending") || !browser.browsingContext;
76+
let docURI = pending ? browser?.currentURI : browser?.documentURI || browser?.currentURI;
77+
let url = docURI?.spec;
78+
let isBlank = !url || url === "about:blank";
79+
if (isBlank || pending) {
80+
this.tooltip.setAttribute("hide-thumbnail", "true");
81+
} else {
82+
await PageThumbs.captureToCanvas(
83+
browser,
84+
canvas,
85+
{
86+
backgroundColor: getComputedStyle(this.thumbBox).getPropertyValue(
87+
"background-color"
88+
),
89+
fullViewport: true,
90+
},
91+
true
92+
);
93+
document.mozSetImageElement("tabThumbImageCanvas", canvas);
94+
this.tooltip.removeAttribute("hide-thumbnail");
95+
}
96+
if (config["Update interval"] > 0) {
97+
this._updateTimer = setTimeout(
98+
() =>
99+
requestIdleCallback(() => requestAnimationFrame(() => this.showPreview(true))),
100+
config["Update interval"]
101+
);
102+
}
103+
return true;
104+
}
105+
cancelTimer() {
106+
if (this._updateTimer) {
107+
clearTimeout(this._updateTimer);
108+
this._updateTimer = null;
109+
}
110+
}
111+
registerSheet() {
112+
const { config } = this;
113+
const css =
114+
/* css */
115+
`#tabThumbTooltip {
116+
--tab-thumb-shadow-size: 6px;
117+
--thumb-border-radius: calc(var(--arrowpanel-border-radius, 7px) - 3px);
118+
appearance: none;
119+
background: transparent;
120+
border: none;
121+
padding: var(--tab-thumb-shadow-size);
122+
}
123+
#tabThumbTooltip[position] {
124+
margin-inline-start: calc(-1 * var(--tab-thumb-shadow-size));
125+
margin-block-start: calc(-1 * var(--tab-thumb-shadow-size));
126+
}
127+
#tabThumbBox {
128+
padding: 5px;
129+
background: var(--arrowpanel-background);
130+
color: var(--arrowpanel-color);
131+
border: 1px solid transparent;
132+
border-radius: var(--arrowpanel-border-radius);
133+
box-shadow: 0 2px var(--tab-thumb-shadow-size) rgba(58,57,68,.2);
134+
}
135+
#tabThumbTooltip[hide-thumbnail] #tabThumbBox {
136+
border-radius: max(3px, var(--thumb-border-radius));
137+
}
138+
#tabThumbLabel {
139+
text-align: center;
140+
margin-inline: 5px;
141+
}
142+
#tabThumbBox > toolbarseparator {
143+
appearance: none;
144+
min-height: 0;
145+
border-top: 1px solid var(--panel-separator-color);
146+
border-bottom: none;
147+
margin: var(--panel-separator-margin);
148+
margin-inline: 0;
149+
padding: 0;
150+
}
151+
#tabThumbTooltip[hide-thumbnail] #tabCanvasBox,
152+
#tabThumbTooltip[hide-thumbnail] #tabThumbBox > toolbarseparator {
153+
display: none;
154+
}
155+
#tabThumbCanvas {
156+
display: block;
157+
border-radius: max(3px, var(--thumb-border-radius));
158+
border: 1px solid var(--arrowpanel-border-color);
159+
height: ${config["Preview height"]}px;
160+
width: ${config["Preview width"]}px;
161+
background-image: -moz-element(#tabThumbImageCanvas);
162+
background-repeat: no-repeat;
163+
background-size: cover;
164+
}`;
165+
let sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(
166+
Ci.nsIStyleSheetService
167+
);
168+
let uri = makeURI("data:text/css;charset=UTF=8," + encodeURIComponent(css));
169+
if (sss.sheetRegistered(uri, sss.AUTHOR_SHEET)) return;
170+
sss.loadAndRegisterSheet(uri, sss.AUTHOR_SHEET);
171+
}
172+
}
173+
174+
if (gBrowserInit.delayedStartupFinished) window.ucTabThumbnail = new TabThumbnail();
175+
else {
176+
let delayedListener = (subject, topic) => {
177+
if (topic == "browser-delayed-startup-finished" && subject == window) {
178+
Services.obs.removeObserver(delayedListener, topic);
179+
window.ucTabThumbnail = new TabThumbnail();
180+
}
181+
};
182+
Services.obs.addObserver(delayedListener, "browser-delayed-startup-finished");
183+
}

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,10 @@ So, this gives you some capabilities not already available in Firefox. In partic
10751075

10761076
</details>
10771077

1078+
#### [Tab Thumbnail Tooltip](/JS/tabThumbnailTooltip.uc.js):
1079+
1080+
Show a large thumbnail image to preview tab content when hovering a tab. Made [by request]("/../../issues/29").
1081+
10781082
#### [Tab Tooltip Navigation Buttons](/JS/tabTooltipNavButtons.uc.js):
10791083

10801084
This script turns the tab tooltip into a mini navigation popup with back, forward, and reload buttons. It still shows the tab's title and URL, and also shows its favicon. So it's similar to the vanilla tooltip, except it's interactive.<details><summary>💬 <i><b>More details...</b></i></summary>

0 commit comments

Comments
 (0)