Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save 0xdevalias/c65f550feb97b5c4aa61433ba473bb43 to your computer and use it in GitHub Desktop.

Select an option

Save 0xdevalias/c65f550feb97b5c4aa61433ba473bb43 to your computer and use it in GitHub Desktop.
Some notes and tools for improving the usability and efficiency of the Gmail web app, often via JavaScript web app reverse engineering / similar.

Gmail Usability Hacks via JavaScript Web App Reverse Engineering

Some notes and tools for improving the usability and efficiency of the Gmail web app, often via JavaScript web app reverse engineering / similar.

Table of Contents

Gmail Shortcut Suggestions / Reminders

Some notes and tools for providing reminders for Gmail keyboard shortcuts / etc.

  • https://support.google.com/mail/answer/6594
  • https://chromewebstore.google.com/detail/keyed-for-gmail/bjffmeipkljmcomfmooofphhncafbhgh
    • Keyed for Gmail

    • Keyed teaches you Gmail's keyboard shortcuts by intercepting your mouse clicks and showing you the faster way. Instead of blocking functionality or overwhelming you with tutorials, Keyed provides just-in-time learning based on your actual Gmail usage.

    • chrome-extension://jifpbeccnghkjeaalbbjmodiffmgedin/crxviewer.html?crx=https%3A%2F%2Fclients2.google.com%2Fservice%2Fupdate2%2Fcrx%3Fresponse%3Dredirect%26os%3Dmac%26arch%3Dx86-64%26os_arch%3Dx86-64%26nacl_arch%3Dx86-64%26prod%3Dchromiumcrx%26prodchannel%3Dunknown%26prodversion%3D9999.0.9999.0%26acceptformat%3Dcrx2%2Ccrx3%26x%3Did%253Dbjffmeipkljmcomfmooofphhncafbhgh%2526uc
  • https://chromewebstore.google.com/detail/Gmail%20Shortcut%20Nudges/nfiaapgfpmpjanmlekaakondnbebnebd
    • Gmail Shortcut Nudges

    • Gmail Shortcut Nudges teaches you to use Gmail's keyboard shortcuts by showing a tooltip displaying what shortcut you can use to trigger the button you're hovering on. It also blocks clicking on the most commonly clicked-on buttons so you're conditioned to use keyboard shortcuts and blaze through your emails.

    • chrome-extension://jifpbeccnghkjeaalbbjmodiffmgedin/crxviewer.html?crx=https%3A%2F%2Fclients2.google.com%2Fservice%2Fupdate2%2Fcrx%3Fresponse%3Dredirect%26os%3Dmac%26arch%3Dx86-64%26os_arch%3Dx86-64%26nacl_arch%3Dx86-64%26prod%3Dchromiumcrx%26prodchannel%3Dunknown%26prodversion%3D9999.0.9999.0%26acceptformat%3Dcrx2%2Ccrx3%26x%3Did%253Dnfiaapgfpmpjanmlekaakondnbebnebd%2526uc
      • let forcedBindings = JSON.parse(`{
            "div[act='19']": { "keys": "u", "name": "Go back" },
            "div[act='7']": { "keys": "e", "name": "Archive" },
            "div[act='10']": { "keys": "#", "name": "Delete" },
            "div[act='2']": { "keys": "shift u", "name": "Mark unread" },
            "div[act='290']": { "keys": "b", "name": "Postpone" },
            "div.adF .h0 > :nth-child(2)": { "keys": "k", "name": "Previous conversation" },
            "div.adF .h0 > :nth-child(3)": { "keys": "j", "name": "Next conversation" },
            "div.T-I-KE": { "keys": "c", "name": "New message" },
            ".aHS-bnt": { "keys": "g,i", "name": "Inbox" },
            ".aHS-bnu": { "keys": "g,t", "name": "Sent" },
            ".aHS-bnq": { "keys": "g,d", "name": "Drafts" },
            ".aaq": { "keys": "r", "name": "Reply (top of conversation)" },
            ".bkH": { "keys": "r", "name": "Reply (bottom of conversation)" },
            "#r": { "keys": "r", "name": "Reply (top of conversation list)" },
            ".bkI": { "keys": "a", "name": "Reply all (bottom of conversation)" },
            "#r2": { "keys": "a", "name": "Reply all (top of conversation list)" },
            ".bkG": { "keys": "f", "name": "Forward (bottom of conversation)" },
            "#r3": { "keys": "f", "name": "Forward (top of conversation list)" },
            ".aoO": { "keys": "cmd enter", "name": "Send" }
        }`);
        
        let optionalBindings = JSON.parse(`{
          "[role='grid']": { "text": "Open: <span class='binding-key'>o</span> or <span class='binding-key'>enter</span>, Down: <span class='binding-key'>j</span> or <span class='binding-key'>↓</span>, Up: <span class='binding-key'>k</span> or <span class='binding-key'>↑</span> <BR>", "name": "Navigating inbox" },
          "[role='grid'] .btb .aXw.T-KT": { "keys": "s", "name": "Star/unstar (inbox)" },
          "[role='grid'] .btb [role='checkbox']": { "keys": "x", "name": "Select email" },
          "form[role='search'] .gb_Se": { "keys": "/", "name": "Search" }
        }`);
        
        //bindings I've discarded:
        // "div.G-tF > :nth-child(4) > :nth-child(1)": { "keys": "v", "name": "Move to" },
        // "div.G-tF > :nth-child(4) > :nth-child(2)": { "keys": "l", "name": "Labels" },
        // "div.G-tF .nf": { "keys": ".", "name": "Other actions" },
        // ".aHS-bnw": { "keys": "g s", "name": "Followed messages" },
        // ".og": { "keys": "meta ⇧ d", "name": "Discard Draft" },
        // ".Hl ~ .Ha": { "keys": "esc", "name": "Save & close" },
        // "[role='navigation'].ajl .aHS-aHO": { "keys": "g,a", "name": "All Mail" },
        // ".pE": { "keys": "meta shift c", "name": "Add CC" },
        // ".pB": { "keys": "meta shift b", "name": "Add BCC" },
        // ".acZ .T-KT": { "keys": "s", "name": "Star/unstar (message view)" },
        // "div[act='9']": { "keys": "!", "name": "Send to spam" },
  • https://chromewebstore.google.com/detail/Simplehuman%2C%20keyboard%20shortcuts%20and%20command%20bar%20for%20Gmail/nipfocapamlefjhldhcagammlldbangf
    • Simplehuman, keyboard shortcuts and command bar for Gmail

    • Advanced keyboard shortcuts, natural language snooze, and smart email management features for Gmail.

    • Simplehuman adds keyboard shortcuts and smart features to Gmail, to make it work like Superhuman.

    • https://www.simplehuman.email/pricing/
      • 15 day free trial, $5/month, $59/year, $119/lifetime
    • https://www.simplehuman.email/learn-to-use/
        1. Learn keyboard shortcuts ⏎

        Learn the keyboard shortcuts for the email actions you frequently use, either from the Command Center or when you are about to click the button.

    • chrome-extension://jifpbeccnghkjeaalbbjmodiffmgedin/crxviewer.html?crx=https%3A%2F%2Fclients2.google.com%2Fservice%2Fupdate2%2Fcrx%3Fresponse%3Dredirect%26os%3Dmac%26arch%3Dx86-64%26os_arch%3Dx86-64%26nacl_arch%3Dx86-64%26prod%3Dchromiumcrx%26prodchannel%3Dunknown%26prodversion%3D9999.0.9999.0%26acceptformat%3Dcrx2%2Ccrx3%26x%3Did%253Dnipfocapamlefjhldhcagammlldbangf%2526uc
      • contentScript.bundle.js (prettified)
        • In module 3719, around lines ~40051 to ~41058 seen to be a bunch of definitions for keybindings/etc
        • In module 8233, around lines ~44204 to ~44293 seem to be a bunch of selectors related to showing the onhover tooltips to remind users of the shortcut keys
          • "Press <span class='binding-key'>".concat(e.keys, '</span>');
        • Around lines ~54849 to ~55744 seem to be definitions for the various commands registered in the Cmd+K toolbar such as archive, open, etc
        • Around lines ~49055 to ~49084 in modules 4014, 9821, 6065, 4610, 6538, 9284; are various i18n translation JSON objects
        • Some libraries it seems to use:

Snooze Email Improvements

Simplehuman adds some UI automation to implement a 'Snooze emails with natural language' feature.

We could leverage a similar pattern of UI automation (or maybe even directly calling the underlying functions / API's to trigger it directly). Even if we don't add a 'natural language' feature, it would be nice to be able to add some configurable 'quick snooze' options for common options (such as 1 min, 5 min, etc).

If we wanted to do something a little more complex, we could also make a 'bounce to top of inbox' type quick snooze feature, that would allow us to set the order the emails should return in, and it would snooze each for +1min to ensure this order when they come back.

At some point Gmail seemed to start flagging snoozed emails as important, but this is almost never something I want done, so it would also be nice to detect when a snoozed email pops back and is marked as important, and unset the important flag on it.

A high level summary (via codex) of how Simplehuman is approaching the UI automation, for inspiration:

codex

Here’s the exact flow the bundled content script uses to drive Gmail’s Snooze UI end-to-end:

Entry Point

  • Orchestrator: U(n) in the Snooze command overlay component (search for “snooze-command-overlay” and U = (function() {).
  • Preconditions:
    • Ensures the feature is enabled via a checkIfFeatureEnabled callback.
    • Parses the user’s natural-language input into a Date via vr(text) (supports “tomorrow”, “this weekend”, weekdays, relative times, locales).
    • Ensures a message is selected in list view:
    • Uses je.s4() (are we in thread list?) and je.uT() (does a thread row look selected?).
    • If needed, simulates the Gmail “select” shortcut via je.dH(xe.xk.select, 'SELF') and waits 100ms.

Open Snooze Menu

  • Triggers Gmail’s built-in Snooze menu using the native keyboard shortcut “b”:
    • je.dH(xe.xk.snooze, 'SELF') sends synthetic keydown/keypress events with the exact key codes Gmail expects for Snooze.
    • Waits ~300ms for the menu to render.

Open Date/Time Picker

  • Function: B() locates and clicks the “Pick date & time”/“Custom date” menu item.
  • How it finds the item:
    • Scans several robust selectors to handle Gmail variants: div[role="menuitem"], .J-N[role="menuitem"], div[jslog*="20281"][role="menuitem"], div.J-N.J-N-JT.
    • Matches on localized text for “custom/pick date” using the extension’s i18n string (snoozeCustomDateOption) plus fallbacks per language:
      • en: “custom”, “pick date”
      • de: “datum und”, “datum auswählen”
      • es: “eligir”, “fecha personalizada”
      • fr: “choisir”, “sélectionner”, “date personnalisée”
      • nl: “kies datum”, “aangepaste datum”
      • pt-BR: “escolher”, “data e hora”
  • If no exact match, it falls back to the last menu item (which is commonly the “custom” option).
  • Click mechanics:
    • Dispatches a series of mouse events on the menu item: mouseover, mousedown, mouseup, click.
    • Also clicks a nested .J-N-J2 or [role="menuitem"] within the item if present.
    • Waits ~1s, then asserts a div[role="dialog"] is present (the date/time picker).

Fill Date and Time

  • Function: xr(date, lang) populates the dialog and submits it.
  • Dialog discovery:
    • Finds the right dialog by scanning div[role="dialog"] that includes a table, input[type="time"], input[type="date"]`, or 2+ inputs, or date/time-ish fields.
  • Input selection (robust fallback sequence):
    • Prefers two inputs in the dialog: first = date input, second = time input.
    • Otherwise searches for:
    • Date: input[type="date"], input[placeholder*="date" i], input[aria-label*="date" i], or generic input-like fallback.
    • Time: input[type="time"], input[placeholder*="time" i], input[aria-label*="time" i], or generic fallback.
  • Formatting the strings to match Gmail’s expected format:
    • Date: Lr(date, placeholderDate, lang) uses the input’s current placeholder/value and locale to format (e.g., de uses DD.MM.YYYY, others use localized “15 May, 2025”/“May 15, 2025” variants). Tr(lang) maps simple codes to locales.
    • Time: Ar(date, placeholderTime) decides 12h vs 24h and zero-padding based on placeholder (e.g., presence of am/pm, leading zeros).
  • Setting values and triggering Gmail change handlers:
    • For both date and time inputs: focus → set value → dispatch input, change, blur events.
    • Waits ~100ms to stabilize.

Submit

  • Finds and clicks the “Save” button:
    • Matches button text (lowercased) against xe.ms.SAVE_BUTTON map:
      • en: “save”
      • de: “speichern”
      • es: “guardar”
      • fr: “enregistrer”
      • nl: “opslaan”
      • pt-BR: “salvar”
  • Fallback: clicks the second element with class mUIrbf-anl if no button text match is found.
  • On success, it tweaks Gmail’s toast text (.bAq) to show “Snoozed until … ⚡️” using a friendly formatter Cr(date) and logs analytics (SNOOZE_EMAIL with ISO datetime and the original text).

Error Handling

  • Parse errors: shows “Could not understand the date format…” and logs SNOOZE_ERROR with PARSE_ERROR.
  • UI errors:
    • If the menu/dialog can’t be opened: “Could not open date picker…”.
    • If inputs can’t be found or set: “Failed to set date and time”.
    • All log SNOOZE_ERROR with OPERATION_ERROR and keep focus in the overlay.

In short: it simulates Gmail’s native keyboard inputs to open Snooze, finds and clicks the “custom date/time” option, programmatically fills date/time inputs in the dialog using localized formatting inferred from placeholders, dispatches input/change/blur to make Gmail accept the values, and then clicks the Save button using localized labels.

See Also

My Other Related Deepdive Gist's and Projects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment