Reference

The settings and the API of askai-button - the options of init(), the data-* attributes, the public functions, the events, the variables of the query template, the definitions of the AI services, adding languages, and the CSS custom properties.

This page describes version 0.1.0. When the file is loaded with a script tag, the functions are on the global object AskAIButton (for example AskAIButton.init()). With npm, you import functions of the same names.

Options of init()

Every option can be left out. A value that cannot be used (wrong type, out of range and so on) is ignored and the default applies.

OptionTypeDefaultMeaning
langstring'auto'Language of the UI. With 'auto', or when left out, it follows the language of the page (see Language).
position'bottom-right' | 'bottom-left''bottom-right'The corner in which the button is shown.
offsetnumber24Distance from the edges of the screen, in px. Zero or more.
zIndexnumber9999Stacking order.
accentColorstring'#222'Colour of the launcher button. Any valid CSS colour. The text is white, so choose a dark colour.
providersArray<string | ProviderDef>all registered servicesThe AI services to offer: ids or definitions, shown in the order given. If none of them is usable, the button is not shown.
defaultProviderstringthe first in the listThe id of the service selected at first. A service the visitor chose before takes precedence.
presetsstring[], or one list per languagenoneExample questions, up to six. Can be written per language as { ja: [...], en: [...] }.
presetAction'fill' | 'send''fill'What a press on an example question does. 'fill' only puts it into the question field; 'send' opens the AI service at once.
maxLengthnumber500Maximum length of a question, in characters. An emoji counts as one character.
maxUrlLengthnumber8000Longest address that may be opened. Beyond it, sending is disabled and "Your question is too long." is shown.
siteUrlstring | nullthe top of the siteValue of {site_url}. When left out: the origin of the current page (https://example.com/), whatever the language or the folder of the page. See "Language" for sites in several languages.
urlstring | nullthe address of the pageValue of {url}. When left out: <link rel="canonical">, then location.href. The hash and utm_* parameters are removed.
mdUrlstring | nullsame as {url}Value of {md_url}. Set it when the page has a Markdown version.
siteNamestring | nullthe name of the siteValue of {site_name}. When left out: <meta property="og:site_name">, then the host name.
queryTemplatestring, or one per language, or nullthe built-in text of the languageThe template of the text handed to the AI.
labelsobject{}Replacements for the wording.
onSend(event) => voidnoneCalled on every send. The argument is the same as detail of askai:send.

Example

AskAIButton.init({
  position: 'bottom-left',
  accentColor: '#27455c',
  providers: ['chatgpt', 'claude'],
  defaultProvider: 'claude',
  presets: {
    ja: ['料金を教えてください', '納期はどのくらいですか?'],
    en: ['How much does it cost?', 'How long does delivery take?'],
  },
  siteUrl: 'https://example.com/shop/',
  onSend: (event) => {
    console.log(event.provider, event.source, event.url);
  },
});

data-* attributes

On a script tag with data-askai, these attributes set the options. All values are written as text.

AttributeOptionHow to write it
data-askai—Makes the file call init() by itself when it is loaded. Needs no value.
data-langlangja, en and so on.
data-presetspresetsSeparated by |.
data-positionpositionbottom-right or bottom-left.
data-offsetoffsetA number.
data-z-indexzIndexA number.
data-accent-coloraccentColorA CSS colour.
data-providersprovidersIds separated by ,.
data-default-providerdefaultProviderAn id.
data-preset-actionpresetActionfill or send.
data-max-lengthmaxLengthA number.
data-site-urlsiteUrlA URL.
data-urlurlA URL.
data-md-urlmdUrlA URL.
data-site-namesiteNameText.

There is no attribute for maxUrlLength, queryTemplate, labels, onSend, definitions of AI services, or presets per language. Call init() to use those.

Functions

FunctionMeaning
init(options)Shows the button. Calling it again rebuilds the button with the new options (the earlier options are not kept).
update(options)Changes some options only. The panel stays open or closed as it was, and the question being typed is kept.
open()Opens the panel.
close()Closes the panel.
destroy()Removes the button and the panel.
registerProvider(def)Adds an AI service, or replaces the definition with the same id. Throws a TypeError when the definition is not valid.
registerLocale(code, dict)Adds a language, or replaces wording of an existing one.
defaults(lang)Returns the built-in wording and the definitions of the AI services (read-only). Without lang, for the language of the page. The result is { lang, labels, providers }.
// Open the panel from a button of your own
document.querySelector('#ask').addEventListener('click', () => AskAIButton.open());

// Look up the built-in text
console.log(AskAIButton.defaults('en').labels.queryTemplate);

Events

These events (each a CustomEvent) are dispatched on document.

EventWhenevent.detail
askai:openThe visitor opened the panel (including through open()).none
askai:sendThe send button was pressed, or an example question was pressed with presetAction: 'send'.{ provider, source, url }
Field of detailMeaning
providerThe id of the chosen AI service, such as chatgpt.
source'preset' (an example question sent as it is) or 'free' (typed by the visitor).
urlThe address of the page the visitor was on (the value of {url}).

The question itself is never passed, neither to the events nor to onSend. The button sends this information nowhere. Whether to record it in your analytics is your decision.

document.addEventListener('askai:send', (event) => {
  const { provider, source, url } = event.detail;
  // for example: record it in the statistics of your own site
});

Query template

The text handed to the AI is made by replacing the variables of a template. Each language has its own built-in template.

{question}

Please refer to the following URL to answer this question.
{site_url}
VariableReplaced by
{question}The visitor's question, without leading and trailing whitespace.
{site_url}The address of the top of the site: the siteUrl option, otherwise the origin of the current page.
{url}The address of the current page: the url option, then <link rel="canonical">, then location.href.
{md_url}The mdUrl option, otherwise the same as {url}.
{title}The title of the page (document.title).
{site_name}The name of the site: the siteName option, then <meta property="og:site_name">, then the host name.

Any other {...} is left as it is. The built-in templates use {question} and {site_url} only. To hand over the current page or its Markdown version, write your own template.

AskAIButton.init({
  queryTemplate: {
    ja: '{question}\n\n次のページを参照して回答してください。\n{url}',
    en: '{question}\n\nPlease refer to this page to answer.\n{url}',
  },
});

The text is URL-encoded and put into the place of {query} in the URL template of the AI service. If the resulting address is longer than maxUrlLength, it cannot be sent.

Definitions of AI services

Three services are registered from the start.

idNameURL templateColour
chatgptChatGPThttps://chatgpt.com/?q={query}#0d7d61
claudeClaudehttps://claude.ai/new?q={query}#b3542f
googleaiGoogle AI Mode (Google AIモード in Japanese)https://www.google.com/search?udm=50&q={query}#1a73e8

These URL formats are not documented by the services and can change without notice. Every field can be replaced through the settings (see "How it works and caveats").

A definition has these fields:

FieldTypeMeaning
idstringIdentifier: letters, digits, _ and -.
namestring, or one per languageThe name shown in the panel.
urlTemplatestringThe address to open. Must start with https:// and contain {query}.
method'url' | 'copy_and_open'How the question is handed over. The default 'url' puts it into the address. 'copy_and_open' copies the text to the clipboard and then opens the address with {query} left empty.
colorstringColour of the selected service and of the send button. The text is white, so choose a dark colour. Without it, #27455c is used.

Changing part of a built-in service

In providers, write the id and only the fields you want to change.

AskAIButton.init({
  providers: [
    'chatgpt',
    { id: 'claude', method: 'copy_and_open' },
    { id: 'googleai', color: '#333' },
  ],
});

Adding an AI service

A service registered with registerProvider() can be named by its id in providers. When providers is left out, all registered services are shown in the order they were registered.

AskAIButton.registerProvider({
  id: 'example',
  name: { ja: 'Example AI', en: 'Example AI' },
  urlTemplate: 'https://ai.example.com/search?q={query}',
  color: '#444',
});

AskAIButton.init({
  providers: ['chatgpt', 'claude', 'googleai', 'example'],
  labels: { 'note.example': 'Your question is sent automatically.' },
});

ai.example.com is a made-up address for this explanation. Before adding a real service, check whether it accepts a question in its URL.

Wording

labels replaces the wording of the button and the panel. These are the keys:

KeyBuilt-in English wording
buttonLabelAsk AI
panelTitleAsk AI about this site
panelSubtitleYour question opens in the AI service you choose. The answer is generated by that service.
inputLabelYour question (for screen readers)
placeholderType your question
presetsLabelExample questions (for screen readers)
providersLabelAI service (for screen readers)
noteOnly the URL of this site is passed. {provider} opens the URL and reads it on the spot.
note.chatgptYour question is sent automatically.
note.claudeYour question is filled in for you. Press send yourself. Sign-in is required.
note.googleaiOpens Google Search in AI Mode. Where AI Mode is unavailable you may see regular search results.
fallbackTitleIf the AI cannot read this site
fallbackStepsCopy the question and URL, then paste them into the AI and send.
copyCopy question and URL
copiedCopied
copyHintQuestion copied. Paste it into {provider} and send.
sendAsk {provider} ↗
closeClose
tooLongYour question is too long.
queryTemplateThe query template

{provider} is replaced by the name of the selected AI service. note.<id> is a remark shown after note while that service is selected.

Wording can be given for all languages at once, or per language.

AskAIButton.init({
  labels: {
    // used in every language
    placeholder: 'e.g. How much is shipping?',
    // per language
    ja: { buttonLabel: 'AIに聞いてみる' },
    en: { buttonLabel: 'Ask an AI' },
  },
});

Language

The language of the UI follows the page, not the settings of the visitor's browser. It is decided in this order:

  1. the lang option, when it is set to something other than 'auto'
  2. <html lang>
  3. the language of the browser (navigator.language)
  4. English

For a language with a region, such as pt-BR, a dictionary is looked for under pt-BR, then pt; without one the UI is in English. The built-in dictionaries are Japanese (ja) and English (en).

presets, queryTemplate and the name of an AI service, when written per language, are looked up under the language of the page, then its base language, then en. Per-language labels are used when they match the language of the page; when the UI is shown in English, those under en are used as well.

The language does not change the address that is handed to the AI. {site_url} is the origin of the page (https://example.com/) on pages of every language, also when each language lives in a folder such as /en/. To hand over the top page of each language instead, set data-site-url (siteUrl in init()) on the pages of that language, for example data-site-url="https://example.com/en/".

Adding a language

Pass a language code and the wording to registerLocale(). Keys you leave out are shown in English.

AskAIButton.registerLocale('fr', {
  buttonLabel: "Demander à l'IA",
  panelTitle: "Interroger l'IA sur ce site",
  placeholder: 'Saisissez votre question',
  send: 'Demander à {provider} ↗',
  close: 'Fermer',
  queryTemplate: "{question}\n\nVeuillez vous référer à l'URL suivante pour répondre à cette question.\n{site_url}",
});

AskAIButton.init();

CSS custom properties

The button and the panel live in a shadow DOM, so the CSS of the page does not affect them. These parts of their look can be changed with CSS custom properties. Set them on :root, for example.

PropertyDefaultMeaning
--askai-accent#222Colour of the launcher button.
--askai-accent-text#fffColour of the text and the icon of the launcher button.
--askai-select#27455cColour of the focus ring, and of the selected service and the send button for a service without a color.
--askai-offset24pxDistance from the edges of the screen.
--askai-z-index9999Stacking order.
--askai-panel-width360pxWidth of the panel. On screens narrower than 480px the panel takes the full width, leaving 16px on each side.
:root {
  --askai-accent: #27455c;
  --askai-panel-width: 400px;
}

When the options accentColor, offset or zIndex are given, the options win.

Hiding the button on a page

A page with this meta tag does not show the button, even when init() is called. Use it when the script is loaded from a shared template and some pages should be left out.

<meta name="askai" content="off">

Single-page applications

  • The address and the title of the page are read at the moment of sending. There is no need to call init() again after a navigation.
  • To change the example questions or other options from one view to the next, use update(). The question being typed is kept, also while the panel is open.
  • <meta name="askai" content="off"> is checked when init(), update(), registerProvider() or registerLocale() is called. Changes of the meta tag are not watched.
  • The button is added directly under document.body. Remove it with destroy() when it is no longer needed.

Browser support

The distributed files use ES2019 syntax. The targets are the last two versions of Chrome, Edge, Firefox and Safari, plus Safari on iOS and Chrome on Android.

Content Security Policy

  • The styles are applied to the shadow DOM with a constructed stylesheet (adoptedStyleSheets). Current browsers need no <style> element for this, so 'unsafe-inline' does not have to be added to style-src. In a browser without constructed stylesheets, a <style> element is created inside the shadow DOM.
  • In script-src, allow the place where askai-button.min.js is served from. For a file loaded from a CDN this is the host of the CDN (https://cdn.jsdelivr.net or https://unpkg.com). With data-askai, no inline script is needed.
  • The button makes no network requests, so nothing has to be set in connect-src.