Skip to content
chocolateboy edited this page Nov 17, 2020 · 10 revisions

This is a draft proposal for a new API for userscript engines, GM_config. See here for the original proposal.

UI

A popup page similar to MonkeyConfig's UI or Greasemonkey 3's options panel (Tools -> Greasemonkey -> Greasemonkey Options...). The page can be opened manually (config.open()) or via a menu command (see below).

Usage

GM_config is a function which takes a config spec (Object). The spec combines features of:

const config = GM_config({
    title: "Example.com Declutterer",

    fields: [
        {
            // field name (required)
            name: "blacklist",

            // optional: defaults to the name in title-case [1]
            // e.g. "disableComments" -> "Disable Comments"
            // [1] https://www.npmjs.com/package/title-case
            label: "Blacklist",

            // "checkbox", "multi", "radio", "select" or "text"
            type: "text",

            // optional default/initial value
            // a primitive, or an array of strings
            // (selected option values)
            value: null,

            // optional description, displayed e.g. in a tooltip
            title: "A comma-separated list of tags to hide",
        },

        {
            name: "disableComments",
            label: "Disable Comments",
            type: "checkbox",
            value: false,
        },

        {
            name: "style",
            label: "Style",
            type: "radio",
            value: "Modern",

            // an array of strings (option values/labels)
            // or { value: ..., label: ... } pairs
            options: ["Compact", "Classic", "Modern"],
        }
    ]
})

// register listeners with `on`
config.on("save", values => { ... }) // values: { blacklist: ..., style: ..., ... }

// open the page with `open` e.g. via a menu command
GM_registerMenuCommand("Configure Example.com Declutterer", () => config.open())

Each entry in the fields array is rendered as a line of form controls. Multiple controls can be included in the same line by grouping them in an array e.g.:

{
    fields: [
        { ... },              // line 1 (1 control)
        [ { ... }, { ... } ], // line 2 (2 controls)
        { ... },              // line 3 (1 control)
    ]
}

It's up to the user to pull default values from storage (via GM_getValue) to populate the UI, and to persist the new values to storage (via GM_setValue) when the save event fires.

Userscripts can portably take advantage of this feature by checking for it with typeof e.g.:

if (typeof GM_config === "function") {
    // ...
} else {
    // fall back to menu commands instead
    GM_registerMenuCommand("Disable Comments", ...)
}

API

Core

Constructor

  • GM_config: (options: Object) ⇒ GMConfig

Methods

  • GMConfig#on
  • GMConfig#open

Events

  • save

Types

  • checkbox
  • multi (or multiselect)
  • radio
  • select
  • text

Optional

Methods

  • GMConfig#close
  • GMConfig#off
  • GMConfig#once

Events

  • cancel
  • close
  • open

Types

  • color
  • date
  • datetime (or time or timestamp)
  • textarea

Notes

Localization

Localization support would be nice (and could be done automatically for the "Save" and "Cancel" buttons).

Unified Controls

Checkbox, radio, multi, and select fields could be a single type (e.g. "select") with an optional style property e.g.:

  • checkbox
  • multi
  • radio

Static Schema

The options are pure JSON (-serializable) and can easily be represented as — and loaded from — a static resource e.g:

const options = JSON.parse(GM_getResourceText("config"))
const config = GM_config(options)

Although it's out of scope for this proposal, this could even be used as a convention to declare config pages statically:

// ==UserScript==
// ...
// @resource GM_config https://example.com/js/config.json
// ...
// ==/UserScript==

See Also