Component to input data into forms by voice

bash
git clone https://github.com/stenciljs/component-starter.git my-component
cd my-component
git remote rm origin
`
and run:
`bash
npm install
npm start
`
Configuration Setup
$3
By default, the component uses a proxy API to handle OpenAI calls server-side. This is the recommended approach for production as it keeps your API keys secure.
1. Start the proxy API server (see api-proxy/README.md)
2. Use the component without providing an api-key:
`html
form-json='{...}'
context="ng"
api-proxy-url="http://localhost:8492"
>
`
$3
For development or specific use cases, you can make direct calls to OpenAI from the browser:
1. Copy the template configuration file:
`bash
cp config.template.js config.js
`
2. Edit config.js and add your OpenAI API key:
`javascript
export const config = {
OPENAI_API_KEY: 'your-actual-api-key-here'
};
`
3. Pass the API key to the component:
`html
form-json='{...}'
context="ng"
api-key="sk-..."
>
`
4. The config.js file is gitignored and will not be committed to the repository
Note: When api-key is provided, the component will use direct API calls. When api-key is not provided, it will use the proxy API at api-proxy-url (default: http://localhost:8492).
To build the component for production, run:
`bash
npm run build
`
To run the unit tests for the components, run:
`bash
npm test
`
Need help? Check out our docs here.
Naming Components
When creating new component tags, we recommend _not_ using stencil in the component name (ex: ). This is because the generated component has little to nothing to do with Stencil; it's just a web component!
Instead, use a prefix that fits your company or any name for a group of related components. For example, all of the Ionic-generated web components use the prefix ion.
Using this component
There are two strategies we recommend for using web components built with Stencil.
The first step for all two of these strategies is to publish to NPM.
You can read more about these different approaches in the Stencil docs.
$3
If your Stencil project is built with the dist output target, you can import a small bootstrap script that registers all components and allows you to load individual component scripts lazily.
For example, given your Stencil project namespace is called my-design-system, to use my-component on any website, inject this into your HTML:
`html
`
This will only load the necessary scripts needed to render . Once more components of this package are used, they will automatically be loaded lazily.
You can also import the script as part of your node_modules in your applications entry file:
`tsx
import 'foobar-design-system/dist/foobar-design-system/foobar-design-system.esm.js';
`
Check out this Live Demo.
$3
If you are using a Stencil component library with dist-custom-elements, we recommend importing Stencil components individually in those files where they are needed.
To export Stencil components as standalone components make sure you have the dist-custom-elements output target defined in your stencil.config.ts.
For example, given you'd like to use as part of a React component, you can import the component directly via:
`tsx
import 'foobar-design-system/my-component';
function App() {
return (
<>
first="Stencil"
middle="'Don't call me a framework'"
last="JS"
>
>
);
}
export default App;
`
---
Theme System
$3
The voice-input-module component supports a complete theme system allowing visual customization via the theme prop (JSON string).
$3
`typescript
{
texts: {
idle: string, // Text displayed at rest
recording: string, // Text displayed during voice recording
processing: string // Text displayed during processing (transcription/LLM)
},
container: {
width: string, // Container width (e.g., "auto", "500px", "100%")
height: string, // Container height (e.g., "auto", "200px")
backgroundColor: string, // Background color (e.g., "#ffffff", "rgb(255,255,255)")
borderColor: string, // Border color
borderRadius: string, // Corner radius (e.g., "8px", "0px", "20px")
padding: string, // Internal spacing (e.g., "20px", "12px", "24px")
gap: string // Spacing between elements (buttons and status) (e.g., "8px", "4px", "12px")
},
buttons: {
voice: {
backgroundColor: string, // Voice button background color
textColor: string, // Text/icon color (currently unused for SVG icon)
hoverColor: string, // Hover color
size: string // Button size (e.g., "50px", "40px", "65px")
},
audio: {
backgroundColor: string, // Audio upload button background color
textColor: string, // Text/icon color
hoverColor: string, // Hover color
size: string // Button size
},
ocr: {
backgroundColor: string, // OCR button background color
textColor: string, // Text/icon color
hoverColor: string, // Hover color
size: string // Button size
}
},
statusText: {
color: string, // Status text color
fontSize: string // Font size (e.g., "14px", "12px", "16px")
}
}
`
$3
`json
{
"texts": {
"idle": "Select an input method",
"recording": "Recording... Click to stop",
"processing": "Processing..."
},
"container": {
"width": "auto",
"height": "auto",
"backgroundColor": "#ffffff",
"borderColor": "#e2e8f0",
"borderRadius": "8px",
"padding": "20px",
"gap": "8px"
},
"buttons": {
"voice": {
"backgroundColor": "#ee4444",
"textColor": "#ffffff",
"hoverColor": "#dd3333",
"size": "50px"
},
"audio": {
"backgroundColor": "#3b82f6",
"textColor": "#ffffff",
"hoverColor": "#2563eb",
"size": "50px"
},
"ocr": {
"backgroundColor": "#10b981",
"textColor": "#ffffff",
"hoverColor": "#059669",
"size": "50px"
}
},
"statusText": {
"color": "#1e293b",
"fontSize": "14px"
}
}
`
$3
#### texts
- idle: Message displayed when no action is in progress
- recording: Message displayed during live voice recording
- processing: Message displayed during audio transcription and form filling by LLM
#### container
- width: Controls main container width. Use "auto" to adapt to content, or a fixed value
- height: Controls main container height
- backgroundColor: Container background color
- borderColor: Container border color (1px border)
- borderRadius: Corner radius (0px = square, high values = very rounded)
- padding: Space between content and container edges
- gap: Vertical space between button row and status text
#### buttons.voice (Live voice recording button)
- backgroundColor: Circular button color
- textColor: Icon color (currently not applied for SVG)
- hoverColor: Mouse hover color
- size: Circular button diameter (e.g., "40px" = small, "65px" = large)
#### buttons.audio (Audio file upload button)
- backgroundColor: SVG icon color
- textColor: Text color (currently unused)
- hoverColor: Hover color
- size: Icon size (width and height)
#### buttons.ocr (OCR button - document upload)
- backgroundColor: OCR button color
- textColor: Text/icon color
- hoverColor: Hover color
- size: Button size
#### statusText
- color: Status text color
- fontSize: Status text font size
$3
The ocr-file-uploader component receives a subset of the theme via its theme prop:
`json
{
"button": {
"size": "50px",
"backgroundColor": "#10b981",
"hoverColor": "#059669",
"textColor": "#ffffff"
}
}
`
These values are extracted from theme.buttons.ocr of the parent component.
To implement in ocr-file-uploader component:
- Apply button.size to button size
- Apply button.backgroundColor to button background color
- Apply button.hoverColor to hover color
- Apply button.textColor to icon/text color
$3
See the demo page (src/index.html) for 5 complete theme examples:
- Medical: Compact & Professional (red, small buttons)
- Nature: Large & Organic (green, large buttons, rounded)
- Tech Dark: Minimal & Terminal (dark background, square corners)
- Corporate: Standard & Professional (gray tones)
- Playful: Fun & Colorful (yellow, emojis, large padding)
$3
#### Using Proxy API (Recommended)
`html
id="my-component"
form-json='{...}'
context="ng"
api-proxy-url="http://localhost:8492"
input-types="['voice', 'audio', 'ocr']"
theme='{"texts":{"idle":"Start recording"},"container":{"backgroundColor":"#f0f9ff"},"buttons":{"voice":{"backgroundColor":"#0ea5e9","size":"60px"}}}'
>
`
#### Using Direct API Calls
`html
id="my-component"
form-json='{...}'
api-key="sk-..."
context="ng"
input-types="['voice', 'audio', 'ocr']"
theme='{"texts":{"idle":"Start recording"},"container":{"backgroundColor":"#f0f9ff"},"buttons":{"voice":{"backgroundColor":"#0ea5e9","size":"60px"}}}'
>
`
Or in JavaScript:
`javascript
const component = document.createElement('voice-input-module');
const theme = {
texts: { idle: "Start recording" },
container: { backgroundColor: "#f0f9ff" },
buttons: { voice: { backgroundColor: "#0ea5e9", size: "60px" } }
};
component.setAttribute('theme', JSON.stringify(theme));
`
$3
1. All properties are optional: If a property is not provided, the default value is used
2. Theme is reactive: Changing the theme attribute updates the component in real-time
3. Colors: Accept all CSS formats (hex, rgb, rgba, color names)
4. Sizes: Must include unit (px, rem, em, %)
5. Width "auto": Recommended for container to adapt to button sizes
6. Gap: Controls vertical spacing between buttons and status text
---
API Proxy Configuration
$3
The voice-input-module component supports two operating modes for OpenAI API calls:
1. Proxy Mode (Recommended): API calls go through a proxy server that securely manages API keys
2. Direct Mode: API calls are made directly from the browser (for development only)
$3
#### Advantages
- ✅ Security: API keys remain server-side
- ✅ Control: Ability to log, limit, or modify requests
- ✅ Production-ready: Recommended for production environments
#### Configuration
1. Start the proxy API server (see api-proxy/README.md)
2. Use the component without providing an api-key:
`html
form-json='{"fields": {...}}'
context="ng"
api-proxy-url="http://localhost:8492"
>
`
#### Parameters
- api-proxy-url: Proxy server URL (default: http://localhost:8492)
- api-key: Do not provide to use proxy mode
#### Endpoints Used
The component will automatically call:
- POST {api-proxy-url}/transcribe for audio transcription
- POST {api-proxy-url}/complete for LLM form filling
$3
#### Advantages
- ✅ Simple: No proxy server needed
- ✅ Development: Ideal for quick testing
#### Disadvantages
- ❌ Security: API key is exposed client-side
- ❌ Not recommended for production
#### Configuration
1. Provide an OpenAI API key to the component:
`html
form-json='{"fields": {...}}'
context="ng"
api-key="sk-proj-..."
>
`
#### Parameters
- api-key: OpenAI API key (starts with sk-)
- api-proxy-url: Ignored when api-key is provided
#### Endpoints Used
The component will directly call:
- POST https://api.openai.com/v1/audio/transcriptions for transcription
- POST https://api.openai.com/v1/chat/completions for LLM
$3
`javascript
if (apiKey) {
// Direct Mode: Direct calls to OpenAI
useProxy = false;
baseUrl = 'https://api.openai.com/v1';
} else {
// Proxy Mode: Calls via proxy server
useProxy = true;
baseUrl = apiProxyUrl || 'http://localhost:8492';
}
`
$3
#### From Direct to Proxy
`diff
form-json='...'
context="ng"
- api-key="sk-proj-..."
+ api-proxy-url="http://localhost:8492"
>
`
#### From Proxy to Direct
`diff
form-json='...'
context="ng"
- api-proxy-url="http://localhost:8492"
+ api-key="sk-proj-..."
>
`
$3
#### Example 1: Production with Proxy
`html
form-json='{"fields": {"name": {"type": "string", "title": "Name"}}}'
context="track"
api-proxy-url="https://api.mycompany.com/openai-proxy"
language="en"
input-types='["voice", "audio"]'
>
`
#### Example 2: Development with Direct
`html
form-json='{"fields": {"name": {"type": "string", "title": "Name"}}}'
context="track"
api-key="sk-proj-xxxxxxxxxxxxx"
language="en"
input-types='["voice", "audio"]'
>
`
$3
#### Component is not making API calls
Check:
1. If api-key is not provided, is the proxy server running?
2. Is the proxy URL correct?
3. Does the proxy server have a valid OpenAI API key in its .env?
#### CORS Error
Proxy Mode: Check the CORS configuration of the proxy server (api-proxy/main.py)
Direct Mode: Direct calls to OpenAI from the browser may be blocked by CORS in some cases
#### Authentication Error
Proxy Mode: Verify that the proxy server has a valid API key in api-proxy/.env`