UNPKG

dictate-button

Version:

Customizable Web Component that adds speech-to-text dictation capabilities to text fields

229 lines (167 loc) 9.29 kB
# Dictate Button ![NPM Version](https://img.shields.io/npm/v/dictate-button) [![Tests](https://github.com/dictate-button/dictate-button/actions/workflows/test.yml/badge.svg)](https://github.com/dictate-button/dictate-button/actions/workflows/test.yml) A customizable web component that adds speech-to-text dictation capabilities to any text input, textarea field, or contenteditable element on your website. Developed for [dictate-button.io](https://dictate-button.io). ## Features - Easy integration with any website - Compatible with any framework (or no framework) - Automatic injection into text fields with the `data-dictate-button-on` attribute (exclusive mode) or without the `data-dictate-button-off` attribute (inclusive mode) - Simple speech-to-text functionality with clean UI - Customizable size and API endpoint - Dark and light theme support - Event-based API for interaction with your application - Built with SolidJS for optimal performance - Accessibility is ensured with ARIA attributes, high-contrast mode support, and clear keyboard focus states ## Supported tags (by our inject scripts) - textarea - input[type="text"] - input[type="search"] - input (without a type; defaults to text) - [contenteditable] elements ## Usage ### Auto-inject modes Choose the auto-inject mode that best suits your needs: | Mode | Description | Scripts | |---|---|---| | Exclusive | Enables for text fields with the `data-dictate-button-on` attribute only. | `inject-exclusive.js` | | Inclusive | Enables for text fields without the `data-dictate-button-off` attribute. | `inject-inclusive.js` | Both auto-inject modes: - Automatically run on DOMContentLoaded (or immediately if the DOM is already loaded). - Watch for DOM changes to apply the dictate button to newly added elements. - Set the button’s language from `document.documentElement.lang` (if present). Long codes like `en-GB` are normalized to `en`. - Position the button to the top right-hand corner of the text field, respecting its padding with 4px fallback if the padding is not set (0). ### From CDN #### Option 1: Using the exclusive auto-inject script In your HTML `<head>` tag, add the following script tag: ```html <script type="module" crossorigin src="https://cdn.dictate-button.io/inject-exclusive.js"></script> ``` Add the `data-dictate-button-on` attribute to any `textarea`, `input[type="text"]`, `input[type="search"]`, `input` without a `type` attribute, or element with the `contenteditable` attribute: ```html <textarea data-dictate-button-on></textarea> <input type="text" data-dictate-button-on /> <input type="search" data-dictate-button-on /> <input data-dictate-button-on /> <div contenteditable data-dictate-button-on /> ``` #### Option 2: Using the inclusive auto-inject script In your HTML `<head>` tag, add the following script tag: ```html <script type="module" crossorigin src="https://cdn.dictate-button.io/inject-inclusive.js"></script> ``` All `textarea`, `input[type="text"]`, `input[type="search"]`, `input` elements without a `type` attribute, and elements with the `contenteditable` attribute that lack `data-dictate-button-off` will be automatically enhanced by default. To disable that for a specific field, add the `data-dictate-button-off` attribute to it this way: ```html <textarea data-dictate-button-off></textarea> <input type="text" data-dictate-button-off /> <input type="search" data-dictate-button-off /> <input data-dictate-button-off /> <div contenteditable data-dictate-button-off /> ``` #### Option 3: Manual integration Import the component and use it directly in your code: ```html <script type="module" crossorigin src="https://cdn.dictate-button.io/dictate-button.js"></script> <dictate-button size="30" api-endpoint="https://api.dictate-button.io/transcribe" language="en"></dictate-button> ``` ### From NPM Import once for your app: ```js // For selected text fields (with data-dictate-button-on attribute): import 'dictate-button/inject-exclusive' // or for all text fields (except those with data-dictate-button-off attribute): import 'dictate-button/inject-inclusive' ``` To choose between **exclusive** and **inclusive** auto-inject modes, see the [Auto-inject modes](#auto-inject-modes) section. ### Advanced usage with library functions If you need more control over when and how the dictate buttons are injected, you can use the library functions directly: Tip: You can also import from subpaths (e.g., 'dictate-button/libs/injectDictateButton') for smaller bundles, if your bundler resolves package subpath exports. ```js import 'dictate-button' // Required when using library functions directly import { injectDictateButton, injectDictateButtonOnLoad } from 'dictate-button/libs' // Inject dictate buttons immediately to matching elements injectDictateButton( 'textarea.custom-selector', // CSS selector for target elements { buttonSize: 30, // Button size in pixels (optional; default: 30) verbose: false, // Log events to console (optional; default: false) customApiEndpoint: 'https://api.example.com/transcribe' // Optional custom API endpoint } ) // Inject on DOM load with mutation observer to catch dynamically added elements injectDictateButtonOnLoad( 'input.custom-selector', // CSS selector for target elements { buttonSize: 30, // Button size in pixels (optional; default: 30) verbose: false, // Log events to console (optional; default: false) customApiEndpoint: 'https://api.example.com/transcribe', // Optional custom API endpoint watchDomChanges: true // Watch for DOM changes (optional; default: false) } ) ``` Note: the injector mirrors the target field’s display/margins into the wrapper, sets wrapper width to 100% for block-level fields, and adds padding to avoid the button overlapping text. The wrapper also has the `dictate-button-wrapper` class for easy styling. ## Events The dictate-button component emits the following events: - `recording:started`: Fired when user starts recording. - `recording:stopped`: Fired when user stops recording. - `recording:failed`: Fired when an error occurs during recording. - `transcribing:started`: Fired when transcribing is started. - `transcribing:finished`: Fired when transcribing is complete. The event detail contains the transcribed text. - `transcribing:failed`: Fired when an error occurs during transcribing. The ideal scenario is when user first starts recording (`recording:started`), then stops recording (`recording:stopped`), then the recorded audio is sent to the server for processing (`transcribing:started`), and finally the transcribed text is received (`transcribing:finished`). > recording:started -> recording:stopped -> transcribing:started -> transcribing:finished In case of an error in recording or transcribing, the `recording:failed` or `transcribing:failed` event is fired, respectively. Example event handling: ```javascript const dictateButton = document.querySelector('dictate-button'); dictateButton.addEventListener('transcribing:finished', (event) => { const transcribedText = event.detail; console.log('Transcribed text:', transcribedText); // Add the text to your input field document.querySelector('#my-input').value += transcribedText; }); ``` ## Attributes | Attribute | Type | Default | Description | |---------------|---------|-----------------------------------------|----------------------------------------| | size | number | 30 | Size of the button in pixels | | apiEndpoint | string | https://api.dictate-button.io/transcribe| API endpoint for transcription service | | language | string | (not set) | Optional language code (e.g., 'en', 'fr', 'de') which may speed up the transcription. | | theme | string | (inherits from page) | 'light' or 'dark' | | class | string | | Custom CSS class | ## Styling You can customize the appearance of the dictate button using CSS parts: ```css /* Style the button container */ dictate-button::part(container) { /* Custom styles */ } /* Style the button itself */ dictate-button::part(button) { /* Custom styles */ } /* Style the button icons */ dictate-button::part(icon) { /* Custom styles */ } ``` ## API Endpoint By default, dictate-button uses the `https://api.dictate-button.io/transcribe` endpoint for speech-to-text conversion. You can specify your own endpoint by setting the `apiEndpoint` attribute. The API expects: - POST request - Multipart form data with the following fields: - `audio`: Audio data as a File (audio/webm format) - `origin`: The origin of the website (automatically added) - `language`: Optional language code (if provided as an attribute) - Response should be JSON with a `text` property containing the transcribed text ## Browser Compatibility The dictate-button component requires the following browser features: - Web Components - MediaRecorder API - Fetch API Works in all modern browsers (Chrome, Firefox, Safari, Edge).