Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zoom Commands #4488

Merged
merged 10 commits into from
Jul 16, 2024
Merged

Zoom Commands #4488

merged 10 commits into from
Jul 16, 2024

Conversation

UncleSnail
Copy link
Contributor

Description

Resolves the issues with chjj's original PR for zoom commands.

This implementation fixes the following issues:

  • Uses Chrome's zoom levels instead of a set increment.
  • Resets to the user's default zoom level instead of always 1.
  • Uses promises instead of callbacks.

It also uses a more similar code style to that of this extension. As expected, I have tested this manually in both Chrome and Firefox, ran the unit tests, and ran the formatter. If there are any suggestions for better implementations or code style, let me know.

Note that it would be very easy to add in an array of the Firefox default zoom levels and switch between the two based on the current browser. This might make the functionality more expected for Firefox users. If you would like me to do this, just let me know. I didn't do it here yet since in the original PR you mentioned to just use Chrome's values for now.

From the original PR:

Implements zoom commands in a manner similar to cVim (which I recently migrated away from as it seems unmaintained).

The three new commands are zoomIn, zoomOut, and zoomReset. These are combined with a configurable zoomStep option which defaults to 0.10 (10%).

The default key mappings for the above commands are zi, zo, and z0 (cVim defaults) -- may be worth bikeshedding over.

Fixes #1866 and #4161 (also see #2978).

@UncleSnail UncleSnail mentioned this pull request Jun 5, 2024
@UncleSnail
Copy link
Contributor Author

Continuing the discussion about possible other default keyboard shortcut options, a friend recommended using "zz" as reset zoom, which is easier to press and has a president from some other programs like the open source Zathura document viewer (note that by default, Zathura supports both zz and z0). It also follows the pattern of double pressing the same key having a function. We can consider this, although z0 might be easier to remember for the general public.

@philc
Copy link
Owner

philc commented Jun 12, 2024 via email

background_scripts/main.js Outdated Show resolved Hide resolved
background_scripts/main.js Outdated Show resolved Hide resolved
background_scripts/main.js Outdated Show resolved Hide resolved
@UncleSnail
Copy link
Contributor Author

Okay @philc, I implemented all the requested changes and I found some more things that you should look at.

  1. I refactored the zoom commands to use nextZoomLevel.
  2. I added unit tests to main_test.js in the Next zoom level context. Please let me know if there was a different place you wanted these.
  3. I implemented the correct zoom levels for Firefox so that it matches Firefox users' expectations.
  4. I performed much more thorough manual testing in Chrome, finding a few Chrome specific bugs.
  5. Chrome allows having zoom set to below and above the maximum, so I fixed the code to properly handle zooming from these cases and added unit tests for this.
  6. Chrome returns a zoom value with floating point errors in some cases, like 33% and 175%, making it impossible to zoom in past these values. I fixed the code to handle this and added unit tests.
  7. Added setZoom value command. Now that we properly handle the edge cases in Chrome and Firefox, it is useful to have a way to let users set their own zoom values, allowing them to, for example, set z1, z2, z3, z4... to their preferred values. This is a feature that I personally want along with the zoom implementation and to me it made sense to round out the zoom feature set. If you would prefer it be in a different PR, or you don't like the feature/implementation, it can easily be removed. Thank you for your patience in reviewing my features. The implementation is simple and I added checks to avoid abuse/breaking when reasonable. If we accept this feature, I will add an FAQ about using it in the wiki.
  8. I realized that tabs.setZoom has a "hidden" built-in behavior to set the zoom level to that tab's default zoom level by setting zoom to 0, making the resetZoom implementation simpler and more performant.

That summarizes the changes.

As usual, I have tried to do extensive testing in both Firefox and Chrome (previously I clearly hadn't tested thoroughly enough in Chrome). The unit tests all pass. I have formatted the code with deno fmt (and only committed the changes it suggested to the files I worked on. Note that there are suggested changes in many other files).

Copy link
Contributor Author

@UncleSnail UncleSnail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are a few code explanations and question/suggestions. They are all also mostly covered in the summary message.

@@ -868,6 +951,7 @@ Object.assign(globalThis, {
HintCoordinator,
BackgroundCommands,
majorVersionHasIncreased,
nextZoomLevel,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that this was how a previous coder had exposed majorVersonHasIncreased for testing purposes, so I did the same for nextZoomLevel. If you have a better preferred method, I'd love to use it.

assert.equal(0.25, nextZoom);
});

should("Test Chrome 33% zoom in with float error", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two floating point error tests only apply to Chrome. Firefox does not seem to have the floating point error issues.

assert.equal(0.25, nextZoom);
});

should("Zoom in from below the minimum", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These next four tests ensure zooming works when we zoom from outside of Chromes min/max zoom range, because Chrome (not Firefox) allows the zoom number to be outside the default accepted range. Supporting this should help avoid issues with other Chromium based browsers that allow different zoom values.

assert.equal(1.00, nextZoom);
});

should("Zoom in past the maximum", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zooming should stop on the min/max boundary.

assert.equal(0.80, nextZoom);
});

should("Zoom in from between values", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zooming from between values should zoom up/down to the nearest value, not skipping.

async setZoom({ tabId, registryEntry }) {
const zoomLevel = registryEntry.optionList[0] ?? 1;
const newZoom = parseFloat(zoomLevel);
// Chrome breaks on values below 0.01 and 10 or above.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chrome allows setting the zoom between 1% and 999%, but it will not actually change the visual size of the page beyond 0.25 and 5.00. I decided to allow setting to any value Chrome allows, which seems the most sensible and should help with support for other Chrome based browsers.

const newZoom = parseFloat(zoomLevel);
// Chrome breaks on values below 0.01 and 10 or above.
if (!isNaN(newZoom) && newZoom >= 0.01 && newZoom < 10) {
chrome.tabs.setZoom(tabId, newZoom);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only try a zoom if we found a reasonable number.
We could also support parsing percents if you would like, so that users who try to enter:
map z1 setZoom 10%
will still have their config parse. That would not be difficult, but I think it's probably the right option might be to only have one correct method to set values.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

return currentZoom;
} else if (steps > 0) { // In
// Chrome sometimes returns values with floating point errors.
currentZoom += 0.0000001; // This is needed to solve floating point bugs in Chrome.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to see if adding a tiny value will boost the number up to a value in our list to cover floating point errors.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does floating point error mean here? isNaN?

Copy link
Contributor Author

@UncleSnail UncleSnail Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an example into the comments to make this meaning clear. There are also unit tests to cover the known values for which Chrome does not return the precise number.
For further clarification, if we run tabs.setZoom(tabId, 0.33) Chrome will show 33% in the UI. However, when we run tabs.getZoom(tabId) Chrome will give us the number 0.32999999999999996

currentZoom += 0.0000001; // This is needed to solve floating point bugs in Chrome.
const nextIndex = zoomLevels.findIndex((level) => level > currentZoom);
// Properly handle index not found (we want not found to be after array, not -1).
const floorIndex = nextIndex < 0 ? zoomLevels.length : nextIndex - 1;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we didn't find the value in our list, IE, it was bigger than all of them, we want to say the index is past the end, not -1. This helps handle several edge cases.

@@ -551,6 +562,11 @@ const commandDescriptions = {
moveTabLeft: ["Move tab to the left", { background: true }],
moveTabRight: ["Move tab to the right", { background: true }],

setZoom: ["Set zoom to the value given after the command, like setZoom 2.25", { background: true }],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to add an example here to make the usage more clear, but if there is a better place for this or a better way to make the usage clear, we should remove it here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming the zoom value must be given as part of the command definition, then documenting that here would be unusual, as this string is shown in the help dialog.

Where to document a command's options is an unsolved issue. I think the #2827 has the most substantial discussion about it. The tips and tricks wiki page contains various command options organized by use case.

For now, I'd suggest this docstring: "Set zoom level to a given value. E.g. map zz setZoom 1.5"

Copy link
Owner

@philc philc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. I plan to merge this once you've had a chance to look at my comments.

background_scripts/main.js Outdated Show resolved Hide resolved
return currentZoom;
} else if (steps > 0) { // In
// Chrome sometimes returns values with floating point errors.
currentZoom += 0.0000001; // This is needed to solve floating point bugs in Chrome.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does floating point error mean here? isNaN?

// Chrome sometimes returns values with floating point errors.
currentZoom += 0.0000001; // This is needed to solve floating point bugs in Chrome.
const nextIndex = zoomLevels.findIndex((level) => level > currentZoom);
// Properly handle index not found (we want not found to be after array, not -1).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this "properly handle" comment is unnecessary if you make the condition on the next line nextIndex == -1 (nextIndex < 0 is actually less clear/specific than nextIndex == -1), and removing it from both branches is a nice DRY cleanup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Originally I needed to catch both -1 and -2, but that issue should be fixed now because I moved the subtraction (now nextIndex - 1) to this line instead.

const newZoom = parseFloat(zoomLevel);
// Chrome breaks on values below 0.01 and 10 or above.
if (!isNaN(newZoom) && newZoom >= 0.01 && newZoom < 10) {
chrome.tabs.setZoom(tabId, newZoom);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.


should("Zoom in from above the maximum", async () => {
const count = 1;
const currentZoom = 9.99; // highest non-broken Chrome zoom level
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does non-broken mean? What happens, exactly, if it's > 9.99?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still scales the page correctly to 500%, but it does not parse the number correctly and shows odd % numbers at the top of the page. Note that in the Chrome API documentation for setZoom it does not specify a range of values to be used. I determined the values it handles well from my own testing.
Firefox DOES specify that the value should be between 0.3 and 5, but it gracefully fails for numbers outside of this range. Chrome isn't quite so graceful. That's why I decided to add a limit where Chrome begins to act oddly, but didn't do anything for Firefox's range.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I don't know why I thought it was parsing the number incorrectly.
I think it does work correctly for bigger values, it's just that there isn't enough space to show the number, so it shows "1,0..." because the "1,000" gets truncated. And it looks like for smaller values it just does nothing, similar to Firefox.
I must have been thinking incorrectly before.
So this bounds checking may be unnecessary. I am going to remove it.

@@ -551,6 +562,11 @@ const commandDescriptions = {
moveTabLeft: ["Move tab to the left", { background: true }],
moveTabRight: ["Move tab to the right", { background: true }],

setZoom: ["Set zoom to the value given after the command, like setZoom 2.25", { background: true }],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming the zoom value must be given as part of the command definition, then documenting that here would be unusual, as this string is shown in the help dialog.

Where to document a command's options is an unsolved issue. I think the #2827 has the most substantial discussion about it. The tips and tricks wiki page contains various command options organized by use case.

For now, I'd suggest this docstring: "Set zoom level to a given value. E.g. map zz setZoom 1.5"

@@ -551,6 +562,11 @@ const commandDescriptions = {
moveTabLeft: ["Move tab to the left", { background: true }],
moveTabRight: ["Move tab to the right", { background: true }],

setZoom: ["Set zoom to the value given after the command, like setZoom 2.25", { background: true }],
zoomIn: ["Increase zoom on current tab", { background: true }],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can drop "on current tab." Our zoom works the same as Chrome's and the Chrome UI doesn't make that distinction.

Although now that I think about it, in Chrome when changing the zoom of a site, Chrome will also update all open tabs which are showing that same site. I haven't yet tested this PR, but presumably our implementation doesn't do that. That would be a nice future change (outside of this PR) to make the behavior consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that wording was left from the original implementation, which tried to limit the zoom to the current tab. It is actually incorrect now, as I had already changed the implementation to match the default behavior. The behavior of all of these zoom commands should match the browser default, and it does zoom all pages of the current domain when you use zi or any other of these commands (I just tested in Chromium to make sure it is still working).

@UncleSnail UncleSnail requested a review from philc July 16, 2024 03:30
Copy link
Owner

@philc philc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work. Looks great. Let's ship it!

Could you rebase this onto master (it looks like there's a merge conflict) and I'll merge it in.

@UncleSnail
Copy link
Contributor Author

Nice work. Looks great. Let's ship it!

Could you rebase this onto master (it looks like there's a merge conflict) and I'll merge it in.

That's odd, I merged master in and don't see any conflicts. I will try to perform a rebase though. Give me a few minutes to see what I can do.

@UncleSnail
Copy link
Contributor Author

Okay @philc I performed a rebase onto master and force pushed. If there are still issues, let me know.

@philc philc merged commit 2bae2f8 into philc:master Jul 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request - Adjust zoom/fontsize via keypress
2 participants