Developers

Native UI: drawers drawn by Pulsar

Send a small JSON spec and Pulsar draws your drawer and settings page with its own controls. They open instantly, follow light and dark mode and the user’s text size, and look like the rest of Pulsar.

The shape of a spec

A SettingSpec is {sections}. An ActionSpec (your drawer) adds a required title, an optional subtitle and an optional header toggle. Each section has an optional title, its items and an optional footnote.

Spec
// ActionSpec — popup.set
{ "title": "Easy Read", "subtitle": "On for news.example",
  "toggle": { "id": "site", "value": true, "title": "Easy Read on news.example" },
  "sections": [ Section, … ] }

// SettingSpec — settings.set
{ "sections": [ Section, … ] }

// Section
{ "title": "Text size" | null, "items": [ Item, … ], "footnote": "…", "card": false }

Every item has a type and a unique id (1–40 of A–Z a–z 0–9 . _ -). The user’s taps come back as popup.action (drawer) or settings.changed (settings page) with that id.

A whole drawer

Easy Read, from the starterA header switch for this site, size chips, one card with a switch, stats and one main button.
Spec
{
  "title": "Easy Read",
  "subtitle": "On for news.example",
  "toggle": {"id": "site", "value": true, "title": "Easy Read on news.example"},
  "sections": [
    {
      "title": "Text size",
      "items": [
        {
          "type": "chips",
          "id": "size",
          "value": "m",
          "options": [
            {"value": "s", "label": "Small"},
            {"value": "m", "label": "Medium"},
            {"value": "l", "label": "Large"}
          ]
        }
      ]
    },
    {
      "title": null,
      "card": true,
      "items": [
        {
          "type": "toggle",
          "id": "spacing",
          "title": "Roomier lines",
          "sub": "More space between lines",
          "value": true
        }
      ]
    },
    {
      "title": "Your week",
      "items": [
        {
          "type": "stats",
          "id": "week",
          "tiles": [
            {"label": "Today", "value": "8", "sub": "pages made easier"},
            {"label": "This week", "bars": [4, 7, 3, 9, 6, 11, 8]}
          ]
        }
      ]
    },
    {
      "title": null,
      "items": [
        {
          "type": "cta",
          "id": "open",
          "label": "See your reading week",
          "primary": true
        }
      ]
    }
  ]
}

toggle

An on/off switch. Sends true or false. enabled: false shows it faded and locked. icon and sub are optional.

Spec
{
  "sections": [
    {
      "title": "Blocking",
      "items": [
        {
          "type": "toggle",
          "id": "ads",
          "title": "Block ads",
          "value": true,
          "sub": "On every site",
          "icon": "shield"
        },
        {
          "type": "toggle",
          "id": "popups",
          "title": "Block pop-ups",
          "value": false,
          "icon": "warning",
          "enabled": false
        }
      ]
    }
  ]
}

choice

One of a list, shown as a row with the current choice; a tap opens the list. Use it for 4 or more options, or long labels. value must be one of the 1–20 options.

Spec
{
  "sections": [
    {
      "title": null,
      "items": [
        {
          "type": "choice",
          "id": "level",
          "title": "Strength",
          "value": "std",
          "icon": "shield",
          "options": [
            {"value": "std", "label": "Standard"},
            {"value": "strict", "label": "Strict"}
          ]
        }
      ]
    }
  ]
}

chips

One of 2–5 short options, all visible. Sends the option’s value. style: "cards" draws equal cards with a colour swatch (#RRGGBB) over each label.

Spec
{
  "sections": [
    {
      "title": "Mode",
      "items": [
        {
          "type": "chips",
          "id": "mode",
          "value": "std",
          "options": [
            {"value": "off", "label": "Off"},
            {"value": "std", "label": "Standard"},
            {"value": "strict", "label": "Strict"}
          ]
        }
      ]
    }
  ]
}
Spec
{
  "sections": [
    {
      "title": null,
      "items": [
        {
          "type": "chips",
          "id": "theme",
          "style": "cards",
          "value": "dark",
          "options": [
            {"value": "dark", "label": "Dark", "swatch": "#212121"},
            {"value": "dim", "label": "Dim", "swatch": "#4A4A4A"},
            {"value": "sepia", "label": "Sepia", "swatch": "#F3EAD7"}
          ]
        }
      ]
    }
  ]
}

slider

A number between min and max (min < max, min ≤ value ≤ max), moving by step, shown with its unit. Sends the number when the finger lifts. In a card: true section it is drawn as the ink slider of Dark Mode.

Spec
{
  "sections": [
    {
      "title": null,
      "card": true,
      "items": [
        {
          "type": "slider",
          "id": "b",
          "title": "Brightness",
          "min": 60,
          "max": 100,
          "value": 90,
          "step": 5,
          "unit": "%"
        },
        {
          "type": "slider",
          "id": "c",
          "title": "Contrast",
          "min": 80,
          "max": 130,
          "value": 105,
          "step": 5,
          "unit": "%"
        }
      ]
    }
  ]
}

text

A text field. hint is the placeholder; multiline: true makes it a box. Sends the string when the user is done. Values up to 4000 characters.

Spec
{
  "sections": [
    {
      "title": null,
      "items": [
        {
          "type": "text",
          "id": "css",
          "title": "Custom CSS",
          "value": "",
          "hint": "a { … }",
          "multiline": true
        }
      ]
    }
  ]
}

button

A row the user taps: open another view, clear something, show a count. value shows at the end of the row. danger: true makes it red. Sends null.

Spec
{
  "sections": [
    {
      "title": null,
      "items": [
        {
          "type": "button",
          "id": "sites",
          "title": "Sites where it’s off",
          "value": "3"
        },
        {
          "type": "button",
          "id": "clear",
          "title": "Clear site data",
          "sub": "Cookies and storage",
          "value": "12 MB",
          "icon": "trash",
          "danger": true
        }
      ]
    }
  ]
}

cta

A full-width button for the drawer’s main action. primary: true is black (white in dark mode); use it once. primary: false is the outlined button. Sends null.

Spec
{
  "sections": [
    {
      "title": null,
      "items": [
        {"type": "cta", "id": "open", "label": "Open dashboard", "primary": true},
        {"type": "cta", "id": "later", "label": "Not now", "primary": false}
      ]
    }
  ]
}

media

A picture card with a title and sub. In API v1 it is drawn as a tile in your tint (images can’t be sent yet; an image field is ignored). A tap sends null.

Spec
{
  "sections": [
    {
      "title": "Up next",
      "items": [
        {
          "type": "media",
          "id": "v1",
          "title": "Mountain Lakes",
          "sub": "Blue Hour · 1080p · 43 min"
        }
      ]
    }
  ]
}

stats

1–4 tiles. A tile shows a value with a label and sub, or bars: up to 14 numbers ≥ 0, drawn as a small bar chart with the last one in ink. Not tappable.

Spec
{
  "sections": [
    {
      "title": null,
      "items": [
        {
          "type": "stats",
          "id": "st",
          "tiles": [
            {"label": "On this page", "value": "14", "sub": "ads and trackers"},
            {"label": "This week", "bars": [3, 5, 2, 8, 6, 9, 14]}
          ]
        }
      ]
    }
  ]
}

A settings page

The same items, sent with settings.set. Pulsar draws them in Settings › Extensions › your name with section titles, icons and footnotes.

Spec
{
  "sections": [
    {
      "title": "Easy Read",
      "items": [
        {
          "type": "toggle",
          "id": "everywhere",
          "title": "On for every site",
          "sub": "Turn it off to pick sites one by one",
          "value": false,
          "icon": "globe"
        },
        {
          "type": "slider",
          "id": "lineHeight",
          "title": "Line height",
          "min": 150,
          "max": 200,
          "step": 10,
          "unit": "%",
          "value": 170
        }
      ]
    },
    {
      "title": null,
      "items": [
        {
          "type": "button",
          "id": "reset",
          "title": "Forget my site choices",
          "sub": "4 sites have their own choice",
          "icon": "trash",
          "danger": true
        }
      ],
      "footnote": "Easy Read never sends anything off your phone."
    }
  ]
}

More than one view

A drawer can show another view: on a button tap, send a different spec with popup.set (Dark Mode’s “Sites where it’s off” list). Add a cta like Done to go back, and go back to your first view on popup.closed.

js
let view = "main";
Pulsar.on("popup.action", async (m) => {
  if (m.item === "offSites") view = "sites";
  if (m.item === "back") view = "main";
  await Pulsar.call("popup.set", view === "sites" ? sitesSpec() : mainSpec(m.host));
});
Pulsar.on("popup.closed", () => { view = "main"; });

Rules and limits

Pulsar checks every spec strictly. A broken spec is refused with bad_params and a message saying what is wrong; the drawer keeps your last good spec.

RuleLimit
SectionsAt most 12
ItemsAt most 40 in all sections together
Item ids1–40 of A–Z a–z 0–9 . _ -, unique; the header toggle’s id differs from all
Texts (titles, subs, labels, footnotes)At most 200 characters
A text item’s valueAt most 4000 characters
choice / chips1–20 options; value must be one of them
slidermin < max and min ≤ value ≤ max
stats1–4 tiles; bars up to 14 numbers ≥ 0
swatch, tint#RRGGBB
Message sizeAt most 64 KB
Answer to popup.openingWithin 400 ms (1.5 s when Pulsar woke you)
  • Settings values: the spec’s values are defaults; Pulsar stores the user’s choices and drops values that don’t fit a new spec.
  • Unknown icon names show no icon. Unknown fields are ignored, so newer fields never break older Pulsars.
  • Pulsar draws your text as you send it. Write it in the user’s language (browser.i18n).

Icons

Use these names in icon fields and in the manifest’s pulsar.icon. The same drawings are in the UI kit’s pulsar-icons.svg for your own pages; the faded ones are only in the kit.

shield
bell
download
eye
eye_off
globe
lock
moon
sun
music
video
image
code
palette
settings
puzzle
bolt
trash
star
search
battery
chat
link
file
info
warning
chevron
chevron_down
close
check
plus
more
back