Skip to content

Commit e75f370

Browse files
committed
options to open UI pages in side panel
1 parent 558cd53 commit e75f370

27 files changed

+530
-206
lines changed

src/_locales/en/messages.json

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,12 +1039,9 @@
10391039
"message": "Max window width for compact UI layout (in pixels)"
10401040
},
10411041
"optionConfigSidePanel": {
1042-
"message": "Side panel for UserCSS configurations this long",
1042+
"message": "UserCSS configuration",
10431043
"description": "Label of the option shown after the number that specifies the minimum length of a UserCSS configuration to be shown in a side panel."
10441044
},
1045-
"optionConfigSidePanelHint": {
1046-
"message": "1..99: minimum number of configuration parameters,\n0: always side panel, you can also right-click or long-press the configuration icon,\n-1: always popup."
1047-
},
10481045
"optionFOUC": {
10491046
"message": "Styles applied too late?",
10501047
"description": "Shown in the options. Followed by FOUCMV2 or FOUCMV3 message."
@@ -1084,6 +1081,24 @@
10841081
"optionKeepAliveNote": {
10851082
"message": "Increase it if you encounter flashing of unstyled content (FOUC) when opening a page after not interacting with the browser for a while.\n\n-1 = ManifestV2 default behavior (no limit).\n0 = ManifestV3 default behavior (half a minute).\n\nThe cache takes 30MB or more of RAM, but rebuilding it may take too much time and cause FOUC depending on the website, network speed, amount of styles, RAM size, and CPU performance."
10861083
},
1084+
"optionSidePanelActions": {
1085+
"message": "Side panel for popup actions"
1086+
},
1087+
"optionSidePanelActionsHint": {
1088+
"message": "Actions to be opened in a side panel when using the popup in standard mode (\"$optPopup$\" option is not enabled).\n\nYou can also right-click or long-press on the corresponding button in the popup.",
1089+
"description": "$optPopup$ is the translated title of \"Open popup in a side panel\"",
1090+
"placeholders": {
1091+
"optPopup": {
1092+
"content": "$1"
1093+
}
1094+
}
1095+
},
1096+
"optionSidePanelNumberHint": {
1097+
"message": "1..99: minimum number of items,\n0: always side panel,\n-1: always popup."
1098+
},
1099+
"optionSidePanelPopup": {
1100+
"message": "Open popup in a side panel"
1101+
},
10871102
"optionTargetIcons": {
10881103
"message": "Icons for target sites",
10891104
"description": "Label for the checkbox that toggles showing icons for style's target sites in various parts of UI"
@@ -1321,6 +1336,15 @@
13211336
"message": "Hold <Enter> (or <▤>) and press a digit to open the corresponding style's menu.",
13221337
"description": "Preserve < and > symbols so that <hotkey> is styled as a key"
13231338
},
1339+
"popupHotkeysInfoSide": {
1340+
"message": "Right-click or long-press on an action button to force-open in a side panel. You can change the default behavior in the options 🞂 $side$.",
1341+
"description": "$side$ is replaced with translation for optionSidePanelActions: \"Side panel for popup actions\"",
1342+
"placeholders": {
1343+
"side": {
1344+
"content": "$1"
1345+
}
1346+
}
1347+
},
13241348
"popupHotkeysInfoTab": {
13251349
"message": "Hold <Alt> to toggle styles only for the current tab.",
13261350
"description": "Preserve < and > symbols so that <hotkey> is styled as a key"
@@ -1345,6 +1369,10 @@
13451369
"message": "Also enabled by detaching editor tab from a browser window,\nand disabled by attaching a single editor tab into another window.",
13461370
"description": "Label for the checkbox controlling 'edit' action behavior in the popup."
13471371
},
1372+
"popupSidePanelOpenHint": {
1373+
"message": "Right-click: in side panel",
1374+
"description": "Tooltip for some buttons in the popup that normally open in a tab/popup."
1375+
},
13481376
"popupStylesFirst": {
13491377
"message": "Styles before commands",
13501378
"description": "Label for the checkbox controlling section order in the popup."

src/background/icon-manager.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import {kDisableAll, kStyleIds} from '@/js/consts';
1+
import {kDisableAll, kSidebar, kStyleIds} from '@/js/consts';
22
import {__values as __prefs, subscribe} from '@/js/prefs';
33
import {CHROME, FIREFOX, MOBILE, VIVALDI} from '@/js/ua';
44
import {debounce, NOP, t} from '@/js/util';
5-
import {browserAction, MF_ICON_EXT, MF_ICON_PATH} from '@/js/util-webext';
5+
import {
6+
browserAction, browserSidebar, MF_ICON_EXT, MF_ICON_PATH, toggleListener,
7+
} from '@/js/util-webext';
68
import * as colorScheme from './color-scheme';
79
import {bgBusy, bgInit, onSchemeChange, onUnload} from './common';
810
import {removePreloadedStyles} from './style-via-webrequest';
@@ -26,8 +28,18 @@ const kShowBadge = 'show-badge';
2628
// https://github.com/openstyles/stylus/issues/335
2729
let hasCanvas = FIREFOX_ANDROID ? false : null;
2830

29-
if (browserAction)
31+
if (browserAction) {
3032
bgInit.push(initIcons);
33+
if (browserSidebar) {
34+
if (__.MV3) // receiving a wake-up event, gonna unregister in subscribe() if not enabled
35+
toggleListener(browserAction.onClicked, true, openSidebar);
36+
subscribe('popup.sidePanel', (key, val) => {
37+
browserAction.setPopup({popup: val ? '' : 'popup.html'});
38+
toggleListener(browserAction.onClicked, val, openSidebar);
39+
}, true);
40+
}
41+
}
42+
3143
onSchemeChange.add(() => {
3244
if (__prefs[kIconset] === -1) {
3345
debounce(refreshGlobalIcon);
@@ -174,6 +186,17 @@ async function loadImage(url) {
174186
return result;
175187
}
176188

189+
function openSidebar(tab) {
190+
tab = {tabId: tab.id};
191+
if (__.MV3) {
192+
browserSidebar.setOptions({path: 'popup.html?' + kSidebar, ...tab});
193+
browserSidebar.open(tab);
194+
} else {
195+
browserSidebar.setPanel({panel: 'popup.html?' + kSidebar, ...tab});
196+
browserSidebar.open();
197+
}
198+
}
199+
177200
function refreshGlobalIcon() {
178201
setIcon({
179202
path: getIconPath(getIconName()),

src/background/popup-data.js

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,65 @@
11
import '@/js/browser';
22
import {kAboutBlank, kPopup, kStyleIds, kTabOvrToggle, kUrl} from '@/js/consts';
3+
import {onConnect, onDisconnect} from '@/js/msg';
34
import {CHROME, FIREFOX} from '@/js/ua';
45
import {ownRoot, supported} from '@/js/urls';
5-
import {getActiveTab} from '@/js/util-webext';
6+
import {getActiveTab, toggleListener} from '@/js/util-webext';
67
import {pingTab} from './broadcast';
78
import {bgBusy} from './common';
89
import reinjectContentScripts from './content-scripts';
910
import {getByUrl} from './style-manager';
10-
import {tabCache, set as tabSet} from './tab-manager';
11+
import {set as tabSet, tabCache} from './tab-manager';
1112
import {waitForTabUrl} from './tab-util';
1213

13-
export default async function makePopupData() {
14+
/** @type {Map<number,Set<chrome.runtime.Port>>} tabs can have popup & popup-in-sidebar */
15+
const popups = new Map();
16+
const onTabUpdated = async (tabId, {url}) => {
17+
if (url && popups.has(tabId)) {
18+
const data = await makePopupData(tabId);
19+
for (const port of popups.get(tabId) || [])
20+
port.postMessage(data);
21+
}
22+
};
23+
/** Using chrome.tabs.onUpdated to see unsupported URLs */
24+
const toggleObserver = enable => toggleListener(chrome.tabs.onUpdated, enable, onTabUpdated);
25+
onConnect[kPopup] = port => {
26+
if (!popups.size)
27+
toggleObserver(true);
28+
const tabId = +port.name.split(':')[1];
29+
const ports = popups.get(tabId);
30+
if (ports) ports.add(port);
31+
else popups.set(tabId, new Set([port]));
32+
};
33+
onDisconnect[kPopup] = port => {
34+
const tabId = +port.name.split(':')[1];
35+
const ports = popups.get(tabId);
36+
if (ports?.delete(port) && !ports.size && popups.delete(tabId) && !popups.size)
37+
toggleObserver(false);
38+
};
39+
40+
export default async function makePopupData(tabId) {
1441
let tmp;
15-
let tab = await getActiveTab();
42+
let tab = await (tabId != null ? browser.tabs.get(tabId) : getActiveTab());
43+
tabId ??= tab.id;
1644
if (FIREFOX && tab.status === 'loading' && tab.url === kAboutBlank) {
17-
tab = await waitForTabUrl(tab.id);
45+
tab = await waitForTabUrl(tabId);
1846
}
1947
// In modern Chrome `url` is for the current tab's contents, so it may be undefined
2048
// when a newly created tab is still connecting to `pendingUrl`.
2149
const url = tab.url || tab.pendingUrl || '';
22-
const td = tabCache[tab.id] || false;
50+
const td = tabCache[tabId] || false;
2351
const isOwn = url.startsWith(ownRoot);
2452
const [
2553
ping0 = __.MV3 && !td[kPopup] && (
26-
tabSet(tab.id, kPopup, true),
54+
tabSet(tabId, kPopup, true),
2755
await reinjectContentScripts(tab)
2856
),
2957
frames,
3058
] = await Promise.all([
3159
isOwn
32-
|| supported(url) && pingTab(tab.id),
60+
|| supported(url) && pingTab(tabId),
3361
isOwn && CHROME && __.BUILD !== 'firefox' && getAllFrames(url, tab)
34-
|| browser.webNavigation.getAllFrames({tabId: tab.id}),
62+
|| browser.webNavigation.getAllFrames({tabId}),
3563
]);
3664
// sorting frames and connecting children to parents
3765
const unknown = new Map(frames.map(f => [f.frameId, f]));
@@ -44,7 +72,7 @@ export default async function makePopupData() {
4472
unknown.set(id, {
4573
frameId: id,
4674
parentFrameId: 0,
47-
styles: getByUrl(frameUrl, undefined, tab.id),
75+
styles: getByUrl(frameUrl, undefined, tabId),
4876
url: frameUrl,
4977
});
5078
}
@@ -79,9 +107,10 @@ export default async function makePopupData() {
79107
if (bgBusy) await bgBusy;
80108
for (const f of frames) {
81109
if (f.url && !f.isDupe)
82-
f.styles ??= getByUrl(f.url, undefined, tab.id);
110+
f.styles ??= getByUrl(f.url, undefined, tabId);
83111
}
84112
}
113+
/** @namespace PopupData */
85114
return {
86115
frames,
87116
ping0,

src/background/set-client-data.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export default async function setClientData({
8282
dark: isDark,
8383
favicon: FIREFOX || isVivaldi,
8484
prefs: nondefaults,
85+
tabId: tabId ?? -1,
8586
[kBadFavs]: (page === 'edit' || page === 'install-usercss' || page === 'manage')
8687
&& prefs.__values['manage.newUI.favicons']
8788
&& prefs.getDbArray(kBadFavs),

src/content/apply.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// WARNING: make sure util-webext.js runs first and sets _deepCopy
22
import {k_deepCopy, kApplyPort} from '@/js/consts';
33
import {onMessage} from '@/js/msg';
4-
import {API, isFrame, isPopup, TDM, updateTDM} from '@/js/msg-api';
4+
import {API, isFrame, isTab, TDM, updateTDM} from '@/js/msg-api';
55
import * as styleInjector from './style-injector';
66
import {FF, isXml, own, ownId, runtime} from './style-injector';
77

@@ -150,7 +150,7 @@ function applyOnMessage(req, sender, multi) {
150150
break;
151151

152152
case 'styleUpdated':
153-
if (req.broadcast && !isPopup || !own.sections && own.cfg.off)
153+
if (req.broadcast && isTab || !own.sections && own.cfg.off)
154154
break;
155155
if (style.enabled) {
156156
getStyles({id: style.id}).then(res =>
@@ -229,7 +229,7 @@ function updateExposeIframes() {
229229

230230
function updateCount() {
231231
let ids, str;
232-
if (TDM < 0 || isPopup) return;
232+
if (TDM < 0 || !isTab) return;
233233
if (isFrame && lazyBadge && performance.now() > 1000) lazyBadge = false;
234234
if (isUnstylable) API.styleViaAPI({method: 'updateCount'});
235235
else if (!throttled

src/edit/edit.css

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -933,12 +933,8 @@ a.icon {
933933
border-right: none;
934934
border-bottom: 1px dashed var(--c65);
935935
padding: $pad05 $pad05 0;
936-
&:not(.sticky) {
937-
flex-direction: row;
938-
flex-wrap: wrap;
939-
}
936+
flex-flow: row wrap;
940937
&.sticky {
941-
flex-direction: row;
942938
box-shadow: 0 0 3rem -.75rem;
943939
#basic-info {
944940
margin: 0;
@@ -957,7 +953,7 @@ a.icon {
957953
font-size: 14px;
958954
}
959955
.settings h2 {
960-
max-width: 10vw;
956+
max-width: max(10vw, 8ex);
961957
}
962958
}
963959
#header.sticky .hide-if-sticky,
@@ -979,6 +975,7 @@ a.icon {
979975
box-sizing: border-box;
980976
display: flex;
981977
flex-wrap: wrap;
978+
gap: $pad05;
982979
#header:not(.sticky) & {
983980
width: 100%;
984981
align-items: center;
@@ -987,9 +984,6 @@ a.icon {
987984
> *:first-child {
988985
flex-grow: 1;
989986
}
990-
> *:not(:last-child) {
991-
margin-right: $pad05;
992-
}
993987
> a {
994988
order: 1;
995989
align-self: center;
@@ -999,13 +993,11 @@ a.icon {
999993
display: flex;
1000994
flex-flow: wrap;
1001995
align-items: center;
996+
gap: $pad025 $pad075;
1002997
}
1003998
#new-as {
1004999
order: 1;
10051000
}
1006-
#basic-info-enabled > :nth-last-child(n + 2) {
1007-
margin-right: $pad075;
1008-
}
10091001
#actions .buttons {
10101002
flex-wrap: nowrap;
10111003
}

src/js/consts.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const kDark = 'dark';
1818
export const kDisableAll = 'disableAll';
1919
export const kEditorSettings = 'editorSettings';
2020
export const kExclusions = 'exclusions';
21+
export const kFind = 'find';
2122
export const kHocused = 'focusedViaClick';
2223
export const kHocusedAttr = 'data-focused-via-click';
2324
export const kInclusions = 'inclusions';
@@ -27,6 +28,7 @@ export const kMainFrame = 'main_frame';
2728
export const kOverridden = 'overridden';
2829
export const kPopup = 'popup';
2930
export const kResolve = 'resolve';
31+
export const kSidebar = 'sidebar';
3032
export const kStyleIdPrefix = 'style-';
3133
export const kStyleIds = 'styleIds';
3234
export const kSubFrame = 'sub_frame';

src/js/dlg/config-dialog.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {UCD} from '@/js/consts';
2-
import {$create, $createLink} from '@/js/dom';
2+
import {$create, $createLink, isSidebar} from '@/js/dom';
33
import {important, messageBox, setupLivePrefs} from '@/js/dom-util';
44
import {breakWord} from '@/js/localization';
55
import {API} from '@/js/msg-api';
@@ -9,7 +9,7 @@ import {MOBILE} from '@/js/ua';
99
import './config-dialog.css';
1010
import '@/css/onoffswitch.css';
1111

12-
export default async function configDialog(style) {
12+
export default async function configDialog(style, y) {
1313
const AUTOSAVE_DELAY = 400;
1414
let saving = false;
1515
let bodyStyle;
@@ -89,7 +89,7 @@ export default async function configDialog(style) {
8989
renderValues();
9090
vars.forEach(renderValueState);
9191
box.style.setProperty('--num', vars.length);
92-
if (isPopup && !MOBILE) {
92+
if (isPopup) {
9393
adjustSizeForPopup(box);
9494
}
9595
updateButtons();
@@ -429,6 +429,12 @@ export default async function configDialog(style) {
429429
let {offsetWidth: width, offsetHeight: height} = contents;
430430
contents.style = '';
431431

432+
if (MOBILE || isSidebar) {
433+
if (y + height < innerHeight)
434+
box.style.cssText = `padding-top:${y}px; justify-content: center;`;
435+
return;
436+
}
437+
432438
const dpr = devicePixelRatio;
433439
const elPicker = document.body.appendChild(
434440
$create('.colorpicker-popup', {style: 'display: none!important'}));
@@ -444,5 +450,6 @@ export default async function configDialog(style) {
444450
bs.cssText = bodyStyle.replace(/((min|max)-width|min-height)\s*:[^;]+|;\s*$/g, '') + `;
445451
min-width:${width}px !important;
446452
min-height:${height}px !important;`;
453+
box.classList.add('center-dialog');
447454
}
448455
}

src/js/dom-init.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {pFavicons, pFaviconsGray} from '@/js/consts';
1+
import {kSidebar, pFavicons, pFaviconsGray} from '@/js/consts';
2+
import {isTab} from '@/js/msg-api';
23
import {ownRoot} from '@/js/urls';
3-
import {$toggleClasses, header} from './dom';
4+
import {$toggleClasses, header, isSidebar} from './dom';
45
import {getCssMediaRuleByName} from './dom-util';
56
import * as prefs from './prefs';
67
import {FIREFOX, MOBILE, OPERA, VIVALDI, WINDOWS} from './ua';
@@ -18,12 +19,13 @@ prefs.subscribe([pFavicons, pFaviconsGray], (key, val) => {
1819
$rootCL.toggle(key === pFavicons ? 'has-favicons' : 'favicons-grayed', val);
1920
}, true);
2021

21-
$root.classList.add(
22+
$rootCL.add(
2223
__.MV3 ? 'mv3' : 'mv2',
2324
MOBILE ? 'mobile' : 'desktop',
2425
WINDOWS ? 'windows' : 'non-windows',
2526
FIREFOX ? 'firefox' : 'chromium',
2627
...OPERA ? ['opera'] : VIVALDI ? ['vivaldi'] : [],
28+
...isSidebar ? [kSidebar] : isTab ? ['tab'] : [],
2729
navigator.maxTouchPoints ? 'touch' : 'non-touch',
2830
);
2931
// set language for a) CSS :lang pseudo and b) hyphenation

0 commit comments

Comments
 (0)