diff --git a/examples/basic-demo/.gitignore b/examples/basic-demo/.gitignore new file mode 100644 index 0000000..d0d4097 --- /dev/null +++ b/examples/basic-demo/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Yarn lock (use parent's lock) +yarn.lock diff --git a/examples/basic-demo/README.md b/examples/basic-demo/README.md new file mode 100644 index 0000000..48068e1 --- /dev/null +++ b/examples/basic-demo/README.md @@ -0,0 +1,33 @@ +# Basic Demo + +A minimal example demonstrating `@syntropy-labs/react-web-speech`. + +## Setup + +```bash +# From the root of the repository +cd examples/basic-demo +yarn install +yarn dev +``` + +## Features Demonstrated + +- `useSpeechInputWithCursor` hook +- Cursor-aware text insertion +- Silence timeout +- Permission state handling +- Start/Stop toggling + +## Note + +This example uses `link:../..` to reference the local package. The parent package must be built first: + +```bash +# From root +yarn build + +# Then run the example +cd examples/basic-demo +yarn dev +``` diff --git a/examples/basic-demo/eslint.config.js b/examples/basic-demo/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/examples/basic-demo/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/examples/basic-demo/index.html b/examples/basic-demo/index.html new file mode 100644 index 0000000..bdb8ca6 --- /dev/null +++ b/examples/basic-demo/index.html @@ -0,0 +1,12 @@ + + + + + + test-speech-app + + +
+ + + diff --git a/examples/basic-demo/package.json b/examples/basic-demo/package.json new file mode 100644 index 0000000..db62255 --- /dev/null +++ b/examples/basic-demo/package.json @@ -0,0 +1,34 @@ +{ + "name": "basic-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@syntropy-labs/react-web-speech": "link:../..", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "npm:rolldown-vite@7.2.5" + }, + "resolutions": { + "vite": "npm:rolldown-vite@7.2.5" + } +} diff --git a/examples/basic-demo/src/App.tsx b/examples/basic-demo/src/App.tsx new file mode 100644 index 0000000..c91d38b --- /dev/null +++ b/examples/basic-demo/src/App.tsx @@ -0,0 +1,48 @@ +import { useRef, useState } from 'react' +import { useSpeechInputWithCursor } from '@syntropy-labs/react-web-speech' + +function App() { + const [value, setValue] = useState('') + const inputRef = useRef(null) + + const { isListening, isSupported, permissionState, toggle, transcript, error } = + useSpeechInputWithCursor({ + inputRef, + value, + onChange: setValue, + appendSpace: true, + silenceTimeout: 3000, + }) + + return ( +
+

🎙️ Speech Input Test

+ +

Supported: {isSupported ? '✅' : '❌'}

+

Permission: {permissionState}

+

Listening: {isListening ? '🔴 Yes' : '⚪ No'}

+ {error &&

Error: {error.message}

} + +