mirror of
https://github.com/zen-browser/desktop.git
synced 2026-01-06 21:37:50 +00:00
feat: Urlbar now supports 'switch space' and 'extensions', b=no-bug, c=common, kbs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
|
||||
index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d67928e64114 100644
|
||||
index fdbab8806fd320f4aacec46a42c8ef953580d00c..f327ca63ff4c99f7aa9acbddc69d2d8b80442d35 100644
|
||||
--- a/browser/components/urlbar/UrlbarView.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
|
||||
@@ -613,7 +613,7 @@ export class UrlbarView {
|
||||
@@ -11,27 +11,25 @@ index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d679
|
||||
// Try to reuse the cached top-sites context. If it's not cached, then
|
||||
// there will be a gap of time between when the input is focused and
|
||||
// when the view opens that can be perceived as flicker.
|
||||
@@ -823,6 +823,19 @@ export class UrlbarView {
|
||||
@@ -823,7 +823,16 @@ export class UrlbarView {
|
||||
// them, resembling tab-to-search. In that case, the input value is
|
||||
// still associated with the first result.
|
||||
this.input.setResultForCurrentValue(firstResult);
|
||||
+ } else if (firstResult.payload.zenAction) {
|
||||
+ this.#selectElement(this.getFirstSelectableElement(), {
|
||||
+ updateInput: false,
|
||||
+ setAccessibleFocus:
|
||||
+ this.controller._userSelectionBehavior == "arrow",
|
||||
+ });
|
||||
+ this.window.setTimeout(() => {
|
||||
- }
|
||||
+ }
|
||||
+ this.window.setTimeout(() => {
|
||||
+ if (queryContext.results[0].payload.zenAction) {
|
||||
+ this.#selectElement(this.getFirstSelectableElement(), {
|
||||
+ updateInput: false,
|
||||
+ setAccessibleFocus:
|
||||
+ this.controller._userSelectionBehavior == "arrow",
|
||||
+ });
|
||||
+ }, 140);
|
||||
}
|
||||
+ }
|
||||
+ }, 10);
|
||||
}
|
||||
|
||||
@@ -1341,7 +1354,7 @@ export class UrlbarView {
|
||||
// Announce tab-to-search results to screen readers as the user types.
|
||||
@@ -1341,7 +1350,7 @@ export class UrlbarView {
|
||||
includeHiddenExposures: true,
|
||||
});
|
||||
let canBeVisible =
|
||||
@@ -40,7 +38,7 @@ index fdbab8806fd320f4aacec46a42c8ef953580d00c..5ed31d5dbfa2e2041e6616f6b036d679
|
||||
if (result.isHiddenExposure) {
|
||||
if (canBeVisible) {
|
||||
this.controller.engagementEvent.addExposure(
|
||||
@@ -3189,7 +3202,7 @@ export class UrlbarView {
|
||||
@@ -3189,7 +3198,7 @@ export class UrlbarView {
|
||||
}
|
||||
|
||||
#enableOrDisableRowWrap() {
|
||||
|
||||
@@ -563,21 +563,52 @@ button.popup-notification-dropmarker {
|
||||
}
|
||||
|
||||
.urlbarView-shortcutContent {
|
||||
border-radius: 4px;
|
||||
padding: 6px 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-left: auto;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 95%);
|
||||
padding: 6px 8px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
font-size: 10px;
|
||||
|
||||
&:empty {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.urlbarView-prettyName,
|
||||
.urlbarView-shortcutContent {
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.urlbarView-prettyName {
|
||||
padding: 4px 6px;
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 90%);
|
||||
margin-left: 6px;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
display: flex;
|
||||
color: color-mix(in srgb, var(--zen-primary-color), currentColor 95%);
|
||||
|
||||
& img {
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
&[workspaceIcon] {
|
||||
scale: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
&[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.urlbarView-row[has-action]:is([type='switchtab'], [type='remotetab'], [type='clipboard']) {
|
||||
& .urlbarView-action:last-child {
|
||||
margin-left: auto !important;
|
||||
@@ -635,14 +666,15 @@ button.popup-notification-dropmarker {
|
||||
--urlbarView-item-inline-padding: 8px;
|
||||
--urlbarView-item-block-padding: 10px;
|
||||
|
||||
&:hover.urlbarView-favicon,
|
||||
&:hover,
|
||||
& .urlbarView-shortcutContent {
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--zen-branding-bg-reverse) 5%,
|
||||
transparent 95%
|
||||
) !important;
|
||||
&:hover {
|
||||
&,
|
||||
& .urlbarView-favicon {
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--zen-branding-bg-reverse) 5%,
|
||||
transparent 95%
|
||||
) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&[selected] {
|
||||
@@ -669,6 +701,10 @@ button.popup-notification-dropmarker {
|
||||
& .urlbarView-shortcutContent {
|
||||
background-color: rgba(255, 255, 255, 0.9) !important;
|
||||
}
|
||||
|
||||
& .urlbarView-prettyName {
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 80%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1377,6 +1377,9 @@ var gZenKeyboardShortcutsManager = {
|
||||
* @returns {string|null} The shortcut as a string or null if not found
|
||||
*/
|
||||
getShortcutDisplayFromCommand(command) {
|
||||
if (!command) {
|
||||
return null;
|
||||
}
|
||||
const shortcut = this.getShortcutFromCommand(command);
|
||||
if (shortcut) {
|
||||
return shortcut.toUserString();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs';
|
||||
import { UrlbarProvider, UrlbarUtils } from 'resource:///modules/UrlbarUtils.sys.mjs';
|
||||
import { globalActions } from 'resource:///modules/ZenUBGlobalActions.sys.mjs';
|
||||
import { ExtensionCommon } from 'resource://gre/modules/ExtensionCommon.sys.mjs';
|
||||
|
||||
const lazy = {};
|
||||
|
||||
@@ -21,6 +22,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
UrlbarTokenizer: 'resource:///modules/UrlbarTokenizer.sys.mjs',
|
||||
QueryScorer: 'resource:///modules/UrlbarProviderInterventions.sys.mjs',
|
||||
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
|
||||
AddonManager: 'resource://gre/modules/AddonManager.sys.mjs',
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
@@ -30,6 +32,8 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
true
|
||||
);
|
||||
|
||||
let { makeWidgetId } = ExtensionCommon;
|
||||
|
||||
/**
|
||||
* A provider that lets the user view all available global actions for a query.
|
||||
*/
|
||||
@@ -63,12 +67,67 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
);
|
||||
}
|
||||
|
||||
#getWorkspaceActions(window) {
|
||||
if (window.gZenWorkspaces.privateWindowOrDisabled) {
|
||||
return [];
|
||||
}
|
||||
const workspaces = window.gZenWorkspaces._workspaceCache?.workspaces;
|
||||
if (!workspaces?.length) {
|
||||
return [];
|
||||
}
|
||||
let actions = [];
|
||||
const activeSpaceUUID = window.gZenWorkspaces.activeWorkspace;
|
||||
for (const workspace of workspaces) {
|
||||
if (workspace.uuid !== activeSpaceUUID) {
|
||||
const accentColor = window.gZenWorkspaces
|
||||
.workspaceElement(workspace.uuid)
|
||||
?.style.getPropertyValue('--zen-primary-color');
|
||||
actions.push({
|
||||
label: 'Focus on',
|
||||
extraPayload: {
|
||||
workspaceId: workspace.uuid,
|
||||
prettyName: workspace.name,
|
||||
prettyIcon: workspace.icon,
|
||||
accentColor,
|
||||
},
|
||||
icon: 'chrome://browser/skin/zen-icons/forward.svg',
|
||||
});
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
async #getExtensionActions(window) {
|
||||
const addons = await lazy.AddonManager.getAddonsByTypes(['extension']);
|
||||
return addons
|
||||
.filter(
|
||||
(addon) =>
|
||||
addon.isActive &&
|
||||
!addon.isSystem &&
|
||||
window.document.getElementById(makeWidgetId(addon.id) + '-BAP')
|
||||
)
|
||||
.map((addon) => {
|
||||
return {
|
||||
icon: 'chrome://browser/skin/zen-icons/extension.svg',
|
||||
label: 'Extension',
|
||||
extraPayload: {
|
||||
extensionId: addon.id,
|
||||
prettyName: addon.name,
|
||||
prettyIcon: addon.iconURL,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Window} window The window to check available actions for.
|
||||
* @returns All the available global actions.
|
||||
*/
|
||||
#getAvailableActions(window) {
|
||||
return globalActions.filter((a) => a.isAvailable(window));
|
||||
async #getAvailableActions(window) {
|
||||
return globalActions
|
||||
.filter((a) => a.isAvailable(window))
|
||||
.concat(this.#getWorkspaceActions(window))
|
||||
.concat(await this.#getExtensionActions(window));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,12 +136,12 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
* @param {string} query The user's search query.
|
||||
*
|
||||
*/
|
||||
#findMatchingActions(query) {
|
||||
async #findMatchingActions(query) {
|
||||
const window = lazy.BrowserWindowTracker.getTopWindow();
|
||||
const actions = this.#getAvailableActions(window);
|
||||
const actions = await this.#getAvailableActions(window);
|
||||
let results = [];
|
||||
for (let action of actions) {
|
||||
const label = action.label;
|
||||
const label = action.extraPayload?.prettyName || action.label;
|
||||
const score = this.#calculateFuzzyScore(label, query);
|
||||
if (score > MINIMUM_QUERY_SCORE) {
|
||||
results.push({
|
||||
@@ -156,7 +215,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionsResults = this.#findMatchingActions(query);
|
||||
const actionsResults = await this.#findMatchingActions(query);
|
||||
if (!actionsResults.length) {
|
||||
return;
|
||||
}
|
||||
@@ -174,6 +233,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
shortcutContent: ownerGlobal.gZenKeyboardShortcutsManager.getShortcutDisplayFromCommand(
|
||||
action.command
|
||||
),
|
||||
...action.extraPayload,
|
||||
});
|
||||
|
||||
let result = new lazy.UrlbarResult(
|
||||
@@ -207,6 +267,9 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
* @returns {object} An object describing the view update.
|
||||
*/
|
||||
getViewUpdate(result) {
|
||||
const prettyIconIsSvg =
|
||||
result.payload.prettyIcon &&
|
||||
(result.payload.prettyIcon.endsWith('.svg') || result.payload.prettyIcon.endsWith('.png'));
|
||||
return {
|
||||
icon: {
|
||||
attributes: {
|
||||
@@ -220,6 +283,27 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
shortcutContent: {
|
||||
textContent: result.payload.shortcutContent || '',
|
||||
},
|
||||
prettyName: {
|
||||
attributes: {
|
||||
hidden: !result.payload.prettyName,
|
||||
style: `--zen-primary-color: ${result.payload.accentColor || 'currentColor'}`,
|
||||
},
|
||||
},
|
||||
prettyNameStrong: {
|
||||
textContent: result.payload.prettyName
|
||||
? prettyIconIsSvg || !result.payload.prettyIcon
|
||||
? result.payload.prettyName
|
||||
: `${result.payload.prettyIcon} ${result.payload.prettyName}`
|
||||
: '',
|
||||
attributes: { dir: 'ltr' },
|
||||
},
|
||||
prettyNameIcon: {
|
||||
attributes: {
|
||||
src: result.payload.prettyIcon || '',
|
||||
hidden: !prettyIconIsSvg || !result.payload.prettyIcon,
|
||||
workspaceIcon: !!result.payload.workspaceId,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -245,6 +329,23 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: 'span',
|
||||
classList: ['urlbarView-prettyName'],
|
||||
hidden: true,
|
||||
name: 'prettyName',
|
||||
children: [
|
||||
{
|
||||
tag: 'img',
|
||||
name: 'prettyNameIcon',
|
||||
attributes: { hidden: true },
|
||||
},
|
||||
{
|
||||
name: 'prettyNameStrong',
|
||||
tag: 'strong',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'shortcutContent',
|
||||
tag: 'span',
|
||||
@@ -258,14 +359,27 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider {
|
||||
const result = details.result;
|
||||
const payload = result.payload;
|
||||
const command = payload.zenCommand;
|
||||
if (!command) {
|
||||
return;
|
||||
}
|
||||
const ownerGlobal = details.element.ownerGlobal;
|
||||
if (typeof command === 'function') {
|
||||
command(ownerGlobal);
|
||||
return;
|
||||
}
|
||||
// Switch workspace if theres a workspaceId in the payload.
|
||||
if (payload.workspaceId) {
|
||||
ownerGlobal.gZenWorkspaces.changeWorkspaceWithID(payload.workspaceId);
|
||||
return;
|
||||
}
|
||||
if (payload.extensionId) {
|
||||
const widgetId = makeWidgetId(payload.extensionId) + '-BAP';
|
||||
const node = ownerGlobal.document.getElementById(widgetId);
|
||||
if (node) {
|
||||
node.doCommand();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!command) {
|
||||
return;
|
||||
}
|
||||
const commandToRun = ownerGlobal.document.getElementById(command);
|
||||
if (commandToRun) {
|
||||
ownerGlobal.gBrowser.selectedBrowser.focus();
|
||||
|
||||
@@ -37,11 +37,22 @@ const globalActionsTemplate = [
|
||||
command: (window) => window.openPreferences(),
|
||||
icon: 'chrome://browser/skin/zen-icons/settings.svg',
|
||||
},
|
||||
{
|
||||
label: 'Open New Window',
|
||||
command: 'cmd_newNavigator',
|
||||
icon: 'chrome://browser/skin/zen-icons/window.svg',
|
||||
},
|
||||
{
|
||||
label: 'Open Private Window',
|
||||
command: 'Tools:PrivateBrowsing',
|
||||
icon: 'chrome://browser/skin/zen-icons/private-window.svg',
|
||||
},
|
||||
];
|
||||
|
||||
export const globalActions = globalActionsTemplate.map((action) => ({
|
||||
...action,
|
||||
isAvailable: (window) => {
|
||||
return window.document.getElementById(action.command)?.getAttribute('disabled') !== 'true';
|
||||
},
|
||||
extraPayload: {},
|
||||
...action,
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user