Page Proxy v0.3.0; Adding GUI tools to the userscript manager

Covering v0.3.x today, to get everything up to speed with active development.

One thing missing

The original premise of Page Proxy was to convert page interactions into userscripts. Right now there is functionality to convert an element into a CSS selector, but the “interaction” part still had to be done manually via writing code.

The workflow for writing a userscript was simple but tedious. You want to modify the page, so you mock what you want the script to do in DevTools, then spend the time looking for the proper CSS selector and writing JS to achieve your functionality. So the goal for this update is to cover the biggest pain point in the workflow: helping you draft code from these page interactions. Part of this pain was handled by the API already, which tries to smooth out some of the quirks of JavaScript (for example, keyboard events behaving completely differently depending on the operating system, browser, etc).

But the goal for this update was a GUI tool that could generate a first draft of your userscript code from your interactions.

So I made the record tool.

Record tool

The record tool combines with the new three dots menu in the Select tool to simulate page interactions.

Upon selecting an element with the select tool, you can use the three dots menu to modify the element, and these interactions are recorded in the recording panel for easy conversion to code.

The three dots menu in the select tool contains these actions:

  • Click element: runs element.click() on the element, triggering a programmatic click.
  • Copy element: uses a variable to internally store the selected element’s HTML as a string.
  • Cut element: same as copy element, but also deletes the element.
  • Paste element: reads the internal clipboard and pastes the element after the selected element.
  • Apply style: Opens the CSS inspector.
  • Hide element: Applies display: none to the element.
  • Delete element: Uses element.remove() to delete the element.

These actions then are displayed the record tool, where they can be converted into code.

The recording tool’s display looks like this:

Afterwards, you can select actions by Ctrl/Cmd+clicking a step, click-and-dragging multiple steps, or clicking the “Select all” button (represented by the two checkmarks button at the bottom right corner). This will bring you to a Convert to code button, which will open the record panel.

In the record panel, you can review the generated code for each step, then at the end, you can do a final pass over all steps!

The panel looks like this:

And supports all the actions in the three dots menu + the select element action. Plus, there’s also multiple option for each supported action, meaning you can pick and choose based on your desired functionality.

All the supported actions convert the immediately-applied page interactions in the three dots select menu into JS code. Step syntax:

async function stepX(variablesFromPreviousSteps) {
  // logic
  return variablesForNextSteps
}

Here are all options and supported actions.

Select element: Must be the first action selected in the record tool, as all other actions depend on the selected element and modify it.

Options:

  • On element matches: default; usually the better pattern as it works with multiple matching elements, while wait until match only runs once.
async function step1() {
  const selector = pq.selector({
    baseSelector: "div.lockin-header",
    matches: e => true
  })
  selector.onElementMatches((selectedElement) => {
    void runAfterStep1(selectedElement)
  })
  return []
}
  • Wait until match
async function step1() {
  const selector = pq.selector({
    baseSelector: "div.lockin-header",
    matches: e => true
  })
  let selectedElement = await selector.waitUntilMatch()
  return [selectedElement]
}

Delete element:

async function step2(selectedElement) {
  selectedElement.remove()
  return []
}

Hide element:

async function step2(selectedElement) {
  ps.applyStyle([selectedElement], {
      "display": "none",
    })
  return [selectedElement]
}

Apply style: based on what gets entered into the CSS Inspector opened from the select menu

async function step2(selectedElement) {
  ps.applyStyle([selectedElement], {
      "cssKey": "cssValue",
  })
  return [selectedElement]
}