Skip to content

SDK

@openpicker/sdk is a tiny, dependency-free client. It does not draw any UI — it starts a pick and awaits the result; the extension owns the picker overlay and the cross-tab routing.

bash
npm install @openpicker/sdk

Quick start

ts
import { createOpenpicker, OpenpickerError } from "@openpicker/sdk"

const op = createOpenpicker({ appName: "My App" })

if (!(await op.isAvailable())) {
  // prompt the user to install the OpenPicker extension
}

try {
  const { selector, matchCount, element, screenshot } = await op.pick({
    url: "https://app.example.com",
    screenshot: "element",
  })
  console.log(selector, `(matches ${matchCount})`, element)
} catch (err) {
  if (err instanceof OpenpickerError && err.code === "cancelled") {
    // the user closed the picker
  } else {
    throw err
  }
}

How it works

Your app calls op.pick({ url }); the extension opens the URL, the user points at any element on the real page, and the selector is routed back — no DevTools, on any web app.

Add visual picking to your product — a tiny SDK lets your users choose page elements without DevTools

Open any website and pick there

Anyone can point and click

Get the selector back in your app

createOpenpicker(options?)

Returns an Openpicker handle.

OptionTypeDefaultDescription
appNamestringDisplay name shown to the user (informational, never trusted).
pingTimeoutnumber1500ms before ping assumes the extension isn't installed.
defaultTimeoutnumber3000ms for quick ops (cancel / highlight / clearHighlight).
targetWindowWindowwindowWindow to communicate over.

Methods

MethodReturnsDescription
ping()PingResultProbe the extension; negotiate version & capabilities.
isAvailable()booleantrue if the extension responds to a ping.
pick(params)PickResultOpen params.url, let the user pick there, resolve with the result.
cancel()voidCancel an in-flight pick (it rejects with cancelled).
highlight(selector)HighlightResultHighlight matches of a selector without entering pick mode.
clearHighlight()voidRemove any active highlight.
activateSelf()voidBring the calling tab to the foreground (a tab can only focus itself).
isTargetOpen()booleanWhether the cross-tab target tab this tab opened is still open.
destroy()voidStop listening and reject any in-flight requests.

pick(params)

FieldTypeDescription
urlstring (required)Page to open and pick in. Omitting it rejects with invalid_params.
screenshot"none" | "element" | "viewport"Screenshot to include. Defaults to "none".
keystringOpaque task id; decides whether a later pick reuses the target tab.
appNamestringOverrides the instance appName for this call.
selectorSelectorConfigSelector-generation rules for this pick (composed with the user's).
lockSelectorSettingsbooleanShow the gear rules read-only. Default false.
lockSelectorEditbooleanMake the selector field read-only. Default false.
requireUniqueMatchbooleanAllow confirm only when the selector matches exactly one element. Default false.
mustMatchstringA CSS selector the picked element must match. Non-matching elements aren't selectable (hovering snaps to the nearest matching ancestor); an invalid value rejects with invalid_params.

Two axes. mustMatch constrains which element can be picked; selector constrains how its selector is built. They're independent and compose — e.g. "only inputs, identified by id":

ts
op.pick({
  url,
  mustMatch: "input, textarea, select, [contenteditable]",
  selector: { class: { enabled: false }, attr: { enabled: false }, tag: { enabled: false } },
})

SelectorConfig — per anchor type (id / class / attr / tag), all optional:

ts
{ id?: SelectorAnchorConfig; class?: …; attr?: …; tag?: … }
// SelectorAnchorConfig: { enabled?: boolean; allow?: string /* regex */; ignore?: string /* regex */ }

selector composes with the user's saved rules — each layer can only narrow. See Configuring selectors.

PickResult

ts
{
  selector: string
  matchCount: number
  element: { tag: string; id?: string; classes: string[]; text?: string; attributes: Record<string, string> }
  screenshot?: string // data: URL, present only when requested
}

pick is cross-tab only — see Cross-tab picking. Error codes are listed in Errors.

matchesSelectorConfig(selector, config)

Returns whether a selector only uses anchors permitted by a SelectorConfig. The user can hand-edit the returned selector (unless you set lockSelectorEdit), so validate it against your rules:

ts
import { matchesSelectorConfig } from "@openpicker/sdk"

const cfg = { attr: { allow: "^data-step$" }, tag: { enabled: false } }
const { selector } = await op.pick({ url, selector: cfg })
if (!matchesSelectorConfig(selector, cfg)) {
  // doesn't meet the requirement — ask the user to pick again
}

Released under the MIT License.