{"id":39937509,"url":"https://github.com/dictate-button/dictate-button","last_synced_at":"2026-01-18T19:01:12.619Z","repository":{"id":306263134,"uuid":"1023739051","full_name":"dictate-button/dictate-button","owner":"dictate-button","description":"Customizable Web Component that adds speech-to-text dictation capabilities to site text fields","archived":false,"fork":false,"pushed_at":"2026-01-16T19:30:21.000Z","size":270,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-17T07:22:27.486Z","etag":null,"topics":["button","custom-element","dictate","dictate-button","dictation","speech-recognition","speech-to-text","transcribe","transcribing","transcription","voice-recognition","voice-to-text","web-component","whisper"],"latest_commit_sha":null,"homepage":"https://dictate-button.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dictate-button.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-21T16:06:41.000Z","updated_at":"2025-11-29T20:07:46.000Z","dependencies_parsed_at":"2025-07-30T06:18:18.767Z","dependency_job_id":"32515b97-22a9-40c2-985d-213559b13c2a","html_url":"https://github.com/dictate-button/dictate-button","commit_stats":null,"previous_names":["kkomelin/dictate-button","dictate-button/dictate-button"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/dictate-button/dictate-button","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictate-button%2Fdictate-button","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictate-button%2Fdictate-button/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictate-button%2Fdictate-button/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictate-button%2Fdictate-button/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dictate-button","download_url":"https://codeload.github.com/dictate-button/dictate-button/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dictate-button%2Fdictate-button/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28548942,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T14:59:57.589Z","status":"ssl_error","status_checked_at":"2026-01-18T14:59:46.540Z","response_time":98,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["button","custom-element","dictate","dictate-button","dictation","speech-recognition","speech-to-text","transcribe","transcribing","transcription","voice-recognition","voice-to-text","web-component","whisper"],"created_at":"2026-01-18T19:00:54.884Z","updated_at":"2026-01-18T19:01:12.581Z","avatar_url":"https://github.com/dictate-button.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dictate Button\n[![NPM Version](https://img.shields.io/npm/v/dictate-button)](https://www.npmjs.com/package/dictate-button)\n[![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)\n\nA customizable web component that adds speech-to-text dictation capabilities to any text input, textarea field, or contenteditable element on your website.\n\nDeveloped for [dictate-button.io](https://dictate-button.io).\n\n## Features\n\n- Easy integration with any website\n- Compatible with any framework (or no framework)\n- Automatic injection into text fields with the `data-dictate-button-on` attribute (exclusive mode) or without the `data-dictate-button-off` attribute (inclusive mode)\n- Simple speech-to-text functionality with clean UI\n- Customizable size and API endpoint\n- Dark and light theme support\n- Event-based API for interaction with your application\n- Built with SolidJS for optimal performance\n- Accessibility is ensured with ARIA attributes, high-contrast mode support, and clear keyboard focus states\n\n## Supported tags (by our inject scripts)\n\n- textarea\n- input[type=\"text\"]\n- input[type=\"search\"]\n- input (without a type; defaults to text)\n- [contenteditable] elements\n\n## Usage\n\n### Auto-inject modes\n\nChoose the auto-inject mode that best suits your needs:\n\n| Mode | Description | Scripts |\n|---|---|---|\n| Exclusive | Enables for text fields with the `data-dictate-button-on` attribute only. | `inject-exclusive.js` |\n| Inclusive | Enables for text fields without the `data-dictate-button-off` attribute. | `inject-inclusive.js` |\n\nBoth auto-inject modes:\n- Automatically run on DOMContentLoaded (or immediately if the DOM is already loaded).\n- Watch for DOM changes to apply the dictate button to newly added elements.\n- Set the button’s language from `document.documentElement.lang` (if present). Long codes like `en-GB` are normalized to `en`.\n- 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).\n\n### From CDN\n\n#### Option 1: Using the exclusive auto-inject script\n\nIn your HTML `\u003chead\u003e` tag, add the following script tag:\n\n```html\n\u003cscript type=\"module\" crossorigin src=\"https://cdn.dictate-button.io/inject-exclusive.js\"\u003e\u003c/script\u003e\n```\n\nAdd 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:\n\n```html\n\u003ctextarea data-dictate-button-on\u003e\u003c/textarea\u003e\n\u003cinput type=\"text\" data-dictate-button-on /\u003e\n\u003cinput type=\"search\" data-dictate-button-on /\u003e\n\u003cinput data-dictate-button-on /\u003e\n\u003cdiv contenteditable data-dictate-button-on /\u003e\n```\n\n#### Option 2: Using the inclusive auto-inject script\n\nIn your HTML `\u003chead\u003e` tag, add the following script tag:\n\n```html\n\u003cscript type=\"module\" crossorigin src=\"https://cdn.dictate-button.io/inject-inclusive.js\"\u003e\u003c/script\u003e\n```\n\nAll `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.\n\nTo disable that for a specific field, add the `data-dictate-button-off` attribute to it this way:\n\n```html\n\u003ctextarea data-dictate-button-off\u003e\u003c/textarea\u003e\n\u003cinput type=\"text\" data-dictate-button-off /\u003e\n\u003cinput type=\"search\" data-dictate-button-off /\u003e\n\u003cinput data-dictate-button-off /\u003e\n\u003cdiv contenteditable data-dictate-button-off /\u003e\n```\n\n#### Option 3: Manual integration\n\nImport the component and use it directly in your code:\n\n```html\n\u003cscript type=\"module\" crossorigin src=\"https://cdn.dictate-button.io/dictate-button.js\"\u003e\u003c/script\u003e\n\n\u003cdictate-button size=\"30\" api-endpoint=\"wss://api.dictate-button.io/v2/transcribe\" language=\"en\"\u003e\u003c/dictate-button\u003e\n```\n\n### From NPM\n\nImport once for your app:\n\n```js\n// For selected text fields (with data-dictate-button-on attribute):\nimport 'dictate-button/inject-exclusive'\n// or for all text fields (except those with data-dictate-button-off attribute):\nimport 'dictate-button/inject-inclusive'\n```\n\nTo choose between **exclusive** and **inclusive** auto-inject modes, see the [Auto-inject modes](#auto-inject-modes) section.\n\n### Advanced usage with library functions\n\nIf you need more control over when and how the dictate buttons are injected, you can use the library functions directly:\n\nTip: You can also import from subpaths (e.g., 'dictate-button/libs/injectDictateButton')\nfor smaller bundles, if your bundler resolves package subpath exports.\n\n```js\nimport 'dictate-button' // Required when using library functions directly\nimport { injectDictateButton, injectDictateButtonOnLoad } from 'dictate-button/libs'\n\n// Inject dictate buttons immediately to matching elements\ninjectDictateButton(\n  'textarea.custom-selector', // CSS selector for target elements\n  {\n    buttonSize: 30,           // Button size in pixels (optional; default: 30)\n    verbose: false,           // Log events to console (optional; default: false)\n    apiEndpoint: 'wss://api.example.com/transcribe' // Optional custom API endpoint\n  }\n)\n\n// Inject on DOM load with mutation observer to catch dynamically added elements\ninjectDictateButtonOnLoad(\n  'input.custom-selector',    // CSS selector for target elements\n  {\n    buttonSize: 30,           // Button size in pixels (optional; default: 30)\n    verbose: false,           // Log events to console (optional; default: false)\n    apiEndpoint: 'wss://api.example.com/transcribe', // Optional custom API endpoint\n    watchDomChanges: true     // Watch for DOM changes (optional; default: false)\n  }\n)\n```\n\nNote: the injector mirrors the target field’s display/margins into the wrapper, \nsets wrapper width to 100% for block-level fields, and adds padding to avoid the button overlapping text.\nThe wrapper also has the `dictate-button-wrapper` class for easy styling.\n\n## Events\n\nThe dictate-button component emits the following events:\n\n- `dictate-start`: Fired when transcription starts (after microphone access is granted and WebSocket connection is established).\n- `dictate-text`: Fired during transcription when text is available. This includes both interim (partial) transcripts that may change and final transcripts. The event detail contains the current transcribed text.\n- `dictate-end`: Fired when transcription ends. The event detail contains the final transcribed text.\n- `dictate-error`: Fired when an error occurs (microphone access denied, WebSocket connection failure, server error, etc.). The event detail contains the error message.\n\nThe typical flow is:\n\n\u003e dictate-start -\u003e dictate-text (multiple times) -\u003e dictate-end\n\nIn case of an error, the `dictate-error` event is fired.\n\nExample event handling:\n\n```javascript\nconst dictateButton = document.querySelector('dictate-button');\n\ndictateButton.addEventListener('dictate-start', () =\u003e {\n  console.log('Transcription started');\n});\n\ndictateButton.addEventListener('dictate-text', (event) =\u003e {\n  const currentText = event.detail;\n  console.log('Current text:', currentText);\n  // Update UI with interim/partial transcription\n});\n\ndictateButton.addEventListener('dictate-end', (event) =\u003e {\n  const finalText = event.detail;\n  console.log('Final transcribed text:', finalText);\n\n  // Add the final text to your input field\n  document.querySelector('#my-input').value += finalText;\n});\n\ndictateButton.addEventListener('dictate-error', (event) =\u003e {\n  const error = event.detail;\n  console.error('Transcription error:', error);\n});\n```\n\n## Attributes\n\n| Attribute     | Type    | Default                                    | Description                            |\n|---------------|---------|--------------------------------------------|-----------------------------------------|\n| size          | number  | 30                                         | Size of the button in pixels           |\n| apiEndpoint   | string  | wss://api.dictate-button.io/v2/transcribe  | WebSockets API endpoint of transcription service |\n| language      | string  | en                                         | Optional [language](https://github.com/dictate-button/dictate-button/wiki/Supported-Languages-and-Dialects) code (e.g., 'fr', 'de') |\n| theme         | string  | (inherits from page)                       | 'light' or 'dark'                      |\n| class         | string  |                                            | Custom CSS class                       |\n\n## Styling\n\nYou can customize the appearance of the dictate button using CSS parts:\n\n```css\n/* Style the button container */\ndictate-button::part(container) {\n  /* Custom styles */\n}\n\n/* Style the button itself */\ndictate-button::part(button) {\n  /* Custom styles */\n}\n\n/* Style the button icons */\ndictate-button::part(icon) {\n  /* Custom styles */\n}\n```\n\n## API Endpoint\n\nBy default, dictate-button uses the `wss://api.dictate-button.io/v2/transcribe` endpoint for real-time speech-to-text streaming.\nYou can specify your own endpoint by setting the `apiEndpoint` attribute.\n\nThe API uses WebSocket for real-time transcription:\n- **Protocol**: WebSocket (wss://)\n- **Connection**: Opens WebSocket connection with optional language query parameter (e.g., `?language=en`)\n- **Audio Format**: PCM16 audio data at 16kHz sample rate, sent as binary chunks\n- **Messages Sent**:\n  - Binary audio data (Int16Array buffers) - Continuous stream of PCM16 audio chunks\n  - `{ type: 'close' }` - JSON message to signal end of audio stream and trigger finalization\n- **Messages Received**: JSON messages with the following types:\n  - `{ type: 'session_opened', sessionId: string, expiresAt: number }` - Session started\n  - `{ type: 'interim_transcript', text: string }` - Interim (partial) transcription result that may change as more audio is processed\n  - `{ type: 'transcript', text: string, turn_order?: number }` - Final transcription result for the current turn\n  - `{ type: 'session_closed', code: number, reason: string }` - Session ended\n  - `{ type: 'error', error: string }` - Error occurred\n\n## Browser Compatibility\n\nThe dictate-button component requires the following browser features:\n- Web Components\n- MediaStream API (getUserMedia)\n- Web Audio API (AudioContext, AudioWorklet)\n- WebSocket API\n\nWorks in all modern browsers (Chrome, Firefox, Safari, Edge).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdictate-button%2Fdictate-button","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdictate-button%2Fdictate-button","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdictate-button%2Fdictate-button/lists"}