├── _dist └── .gitkeep ├── .assets ├── nupkg-icon.png └── nupkg-icon.docx ├── SampleSites ├── SampleSite.Client │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── index.html │ │ └── css │ │ │ └── blazor-ui.css │ ├── Program.cs │ ├── SampleSite.Client.csproj │ ├── Properties │ │ └── launchSettings.json │ └── .vscode │ │ ├── tasks.json │ │ └── launch.json ├── SampleSite.Server │ ├── wwwroot │ │ ├── favicon.ico │ │ └── css │ │ │ └── blazor-ui.css │ ├── appsettings.json │ ├── appsettings.Development.json │ ├── SampleSite.Server.csproj │ ├── Properties │ │ └── launchSettings.json │ ├── Program.cs │ ├── Pages │ │ └── _Host.cshtml │ └── .vscode │ │ ├── tasks.json │ │ └── launch.json ├── Net8App │ └── BlazorApp1 │ │ ├── BlazorApp1 │ │ ├── Components │ │ │ ├── Pages │ │ │ │ ├── Home.razor │ │ │ │ └── Error.razor │ │ │ ├── Routes.razor │ │ │ ├── _Imports.razor │ │ │ ├── App.razor │ │ │ └── Layout │ │ │ │ ├── MainLayout.razor │ │ │ │ ├── NavMenu.razor │ │ │ │ ├── MainLayout.razor.css │ │ │ │ └── NavMenu.razor.css │ │ ├── wwwroot │ │ │ ├── favicon.png │ │ │ └── app.css │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── BlazorApp1.csproj │ │ ├── Program.cs │ │ └── Properties │ │ │ └── launchSettings.json │ │ └── BlazorApp1.Client │ │ ├── wwwroot │ │ ├── appsettings.json │ │ └── appsettings.Development.json │ │ ├── Pages │ │ ├── SpeechRecognitionOnAutoPage.razor │ │ └── SpeechRecognitionOnServerPage.razor │ │ ├── Program.cs │ │ ├── _Imports.razor │ │ ├── BlazorApp1.Client.csproj │ │ └── Components │ │ └── SpeechRecognitionComponent.razor └── SampleSite.Components │ ├── _Imports.razor │ ├── SampleSite.Components.csproj │ ├── Properties │ └── launchSettings.json │ └── App.razor ├── Toolbelt.Blazor.SpeechRecognition ├── PackageSrc │ ├── BuildMultiTargeting │ │ └── Toolbelt.Blazor.SpeechRecognition.targets │ ├── BuildTransitive │ │ └── Toolbelt.Blazor.SpeechRecognition.targets │ └── Build │ │ └── Toolbelt.Blazor.SpeechRecognition.targets ├── SpeechRecognitionAlternative.cs ├── SpeechRecognitionEventArgs.cs ├── SpeechRecognitionStartOptions.cs ├── script.ts ├── ILLink.Substitutions.xml ├── SpeechRecognitionResult.cs ├── SpeechRecognitionOptions.cs ├── bundleconfig.json ├── wwwroot │ ├── script.module.min.js │ └── script.min.js ├── ILLink.Descriptors.xml ├── script.js ├── script.module.js ├── SpeechRecognitionExtensions.cs ├── script.module.ts ├── Toolbelt.Blazor.SpeechRecognition.csproj ├── tsconfig.json └── SpeechRecognition.cs ├── nuget.config ├── RELEASE-NOTES.txt ├── .gitattributes ├── README.md ├── Toolbelt.Blazor.SpeechRecognition.sln ├── .gitignore └── LICENSE /_dist/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.assets/nupkg-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/HEAD/.assets/nupkg-icon.png -------------------------------------------------------------------------------- /.assets/nupkg-icon.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/HEAD/.assets/nupkg-icon.docx -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/HEAD/SampleSites/SampleSite.Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/HEAD/SampleSites/SampleSite.Server/wwwroot/favicon.ico -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/HEAD/SampleSites/Net8App/BlazorApp1/BlazorApp1/wwwroot/favicon.png -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/PackageSrc/BuildMultiTargeting/Toolbelt.Blazor.SpeechRecognition.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/PackageSrc/BuildTransitive/Toolbelt.Blazor.SpeechRecognition.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/Pages/SpeechRecognitionOnAutoPage.razor: -------------------------------------------------------------------------------- 1 | @page "/speech-recognition/auto" 2 | 3 |

Speech Recognition on Auto

4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/Pages/SpeechRecognitionOnServerPage.razor: -------------------------------------------------------------------------------- 1 | @page "/speech-recognition/server" 2 | 3 |

Speech Recognition on Server

4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognitionAlternative.cs: -------------------------------------------------------------------------------- 1 | namespace Toolbelt.Blazor.SpeechRecognition; 2 | 3 | public class SpeechRecognitionAlternative 4 | { 5 | public string? Transcript { get; set; } 6 | 7 | public double Confidence { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.JSInterop 6 | @using SampleSite.Components 7 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognitionEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Toolbelt.Blazor.SpeechRecognition; 2 | 3 | public class SpeechRecognitionEventArgs 4 | { 5 | public int ResultIndex { get; set; } 6 | 7 | public SpeechRecognitionResult[]? Results { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft": "Warning", 7 | "Microsoft.Hosting.Lifetime": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognitionStartOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Toolbelt.Blazor.SpeechRecognition; 2 | 3 | internal class SpeechRecognitionStartOptions 4 | { 5 | public string? Lang { get; set; } 6 | public bool Continuous { get; set; } 7 | public bool InterimResults { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 2 | using Toolbelt.Blazor.Extensions.DependencyInjection; 3 | 4 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 5 | builder.Services.AddSpeechRecognition(); 6 | 7 | await builder.Build().RunAsync(); 8 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/script.ts: -------------------------------------------------------------------------------- 1 | namespace Toolbelt.Blazor.SpeechRecognition { 2 | const searchParam = document.currentScript?.getAttribute('src')?.split('?')[1] || ''; 3 | export var ready = import('./script.module.min.js?' + searchParam).then(m => { 4 | (SpeechRecognition as any).createInstance = m.createInstance; 5 | }); 6 | } 7 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/ILLink.Substitutions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Routes.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/SampleSite.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /RELEASE-NOTES.txt: -------------------------------------------------------------------------------- 1 | v.1.0.0 2 | - Support to .NET 8 and IL trimming. 3 | 4 | v.0.0.5-alpha 5 | - Fix: the "DisableClientScriptAutoInjection" option is not assignable. 6 | 7 | v.0.0.4-alpha 8 | - Upgrade Blazor to v.3.1.0 Preview 4. 9 | - Add support for Blazor Server App (Server-side Blazor). 10 | 11 | v.0.0.3-alpha 12 | - Upgrade Blazor to v.3.0.0 Preview 9. 13 | 14 | v.0.0.2-alpha 15 | - Upgrade Blazor to v.3.0.0 Preview 6. 16 | 17 | v.0.0.1-alpha 18 | - 1st release. -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognitionResult.cs: -------------------------------------------------------------------------------- 1 | //using System.ComponentModel; 2 | 3 | namespace Toolbelt.Blazor.SpeechRecognition; 4 | 5 | public class SpeechRecognitionResult 6 | { 7 | public bool IsFinal { get; set; } 8 | 9 | //[EditorBrowsable(EditorBrowsableState.Never)] 10 | public SpeechRecognitionAlternative[]? Items { get; set; } 11 | 12 | // public SpeechRecognitionAlternative this[int index] { get => this.Items[index]; } 13 | } 14 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorApp1 10 | @using BlazorApp1.Client 11 | @using BlazorApp1.Components 12 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognitionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Toolbelt.Blazor.SpeechRecognition; 2 | 3 | /// 4 | /// Configuration options for "SpeechRecognition" service. 5 | /// 6 | public class SpeechRecognitionOptions 7 | { 8 | /// 9 | /// Gets or sets a value that determines whether or not to inject client script automatically for "SpeechRecognition" service. 10 | /// 11 | public bool DisableClientScriptAutoInjection { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputFiles": [ "script.js" ], 4 | "outputFileName": "wwwroot/script.min.js", 5 | "minify": { 6 | "enabled": false, 7 | "renameLocals": true 8 | }, 9 | "sourceMap": false 10 | }, 11 | { 12 | "inputFiles": [ "script.module.js" ], 13 | "outputFileName": "wwwroot/script.module.min.js", 14 | "minify": { 15 | "enabled": true, 16 | "renameLocals": true 17 | }, 18 | "sourceMap": false 19 | } 20 | ] -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/BlazorApp1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/wwwroot/script.module.min.js: -------------------------------------------------------------------------------- 1 | export const createInstance=n=>{const r=window,u=r.webkitSpeechRecognition||r.SpeechRecognition,t=u?new u:null,i=()=>!1,f=n.invokeMethodAsync.bind(n);return t?(t.onresult=n=>{f("_OnResult",{resultIndex:n.resultIndex,results:Array.from(n.results).map(n=>({isFinal:n.isFinal,items:Array.from(n).map(n=>({confidence:n.confidence,transcript:n.transcript}))}))})},t.onend=()=>f("_OnEnd"),{available:()=>!0,start:n=>Object.assign(t,n).start(),stop:()=>t.stop()}):{available:i,start:i,stop:i}}; -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorApp1.Client 10 | @using BlazorApp1.Client.Components 11 | @using BlazorApp1.Client.Pages 12 | @using Toolbelt.Blazor.SpeechRecognition 13 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Web; 2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 3 | using SampleSite.Components; 4 | using Toolbelt.Blazor.Extensions.DependencyInjection; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.RootComponents.Add("head::after"); 9 | builder.Services.AddSpeechRecognition(options => 10 | { 11 | // options.DisableClientScriptAutoInjection = true; 12 | }); 13 | 14 | await builder.Build().RunAsync(); 15 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/ILLink.Descriptors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Components/SampleSite.Components.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/PackageSrc/Build/Toolbelt.Blazor.SpeechRecognition.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/script.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var Toolbelt; 3 | (function (Toolbelt) { 4 | var Blazor; 5 | (function (Blazor) { 6 | var SpeechRecognition; 7 | (function (SpeechRecognition) { 8 | const searchParam = document.currentScript?.getAttribute('src')?.split('?')[1] || ''; 9 | SpeechRecognition.ready = import('./script.module.min.js?' + searchParam).then(m => { 10 | SpeechRecognition.createInstance = m.createInstance; 11 | }); 12 | })(SpeechRecognition = Blazor.SpeechRecognition || (Blazor.SpeechRecognition = {})); 13 | })(Blazor = Toolbelt.Blazor || (Toolbelt.Blazor = {})); 14 | })(Toolbelt || (Toolbelt = {})); 15 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/wwwroot/script.min.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var Toolbelt; 3 | (function (Toolbelt) { 4 | var Blazor; 5 | (function (Blazor) { 6 | var SpeechRecognition; 7 | (function (SpeechRecognition) { 8 | const searchParam = document.currentScript?.getAttribute('src')?.split('?')[1] || ''; 9 | SpeechRecognition.ready = import('./script.module.min.js?' + searchParam).then(m => { 10 | SpeechRecognition.createInstance = m.createInstance; 11 | }); 12 | })(SpeechRecognition = Blazor.SpeechRecognition || (Blazor.SpeechRecognition = {})); 13 | })(Blazor = Toolbelt.Blazor || (Toolbelt.Blazor = {})); 14 | })(Toolbelt || (Toolbelt = {})); 15 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/SampleSite.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:57524/", 7 | "sslPort": 44382 8 | } 9 | }, 10 | "profiles": { 11 | "https": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "applicationUrl": "http://localhost:57528/;https://localhost:57529/" 18 | }, 19 | "IIS Express": { 20 | "commandName": "IISExpress", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:57736", 7 | "sslPort": 44368 8 | } 9 | }, 10 | "profiles": { 11 | "https": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "applicationUrl": "http://localhost:57530/;https://localhost:57531/" 18 | }, 19 | "IIS Express": { 20 | "commandName": "IISExpress", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Components/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:53521/", 7 | "sslPort": 44383 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleSite.Components": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/BlazorApp1.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Toolbelt.Blazor.Extensions.DependencyInjection; 2 | 3 | var builder = WebApplication.CreateBuilder(args); 4 | 5 | // Add services to the container. 6 | builder.Services.AddRazorPages(); 7 | builder.Services.AddServerSideBlazor(); 8 | builder.Services.AddSpeechRecognition(options => 9 | { 10 | // options.DisableClientScriptAutoInjection = true; 11 | }); 12 | 13 | var app = builder.Build(); 14 | 15 | // Configure the HTTP request pipeline. 16 | if (!app.Environment.IsDevelopment()) 17 | { 18 | app.UseExceptionHandler("/Error"); 19 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 20 | app.UseHsts(); 21 | } 22 | 23 | app.UseHttpsRedirection(); 24 | 25 | app.UseStaticFiles(); 26 | 27 | app.UseRouting(); 28 | 29 | app.MapBlazorHub(); 30 | app.MapFallbackToPage("/_Host"); 31 | 32 | app.Run(); 33 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Blazor SpeechSynthesis Sample Site - WebAssembly App 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 | An unhandled error has occurred. 24 | Reload 25 | 🗙 26 |
27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorApp1.Components; 2 | using Toolbelt.Blazor.Extensions.DependencyInjection; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // Add services to the container. 7 | builder.Services.AddRazorComponents() 8 | .AddInteractiveServerComponents() 9 | .AddInteractiveWebAssemblyComponents(); 10 | 11 | builder.Services.AddSpeechRecognition(); 12 | 13 | var app = builder.Build(); 14 | 15 | // Configure the HTTP request pipeline. 16 | if (app.Environment.IsDevelopment()) 17 | { 18 | app.UseWebAssemblyDebugging(); 19 | } 20 | else 21 | { 22 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 23 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 24 | app.UseHsts(); 25 | } 26 | 27 | app.UseHttpsRedirection(); 28 | 29 | app.UseStaticFiles(); 30 | app.UseAntiforgery(); 31 | 32 | app.MapRazorComponents() 33 | .AddInteractiveServerRenderMode() 34 | .AddInteractiveWebAssemblyRenderMode() 35 | .AddAdditionalAssemblies(typeof(BlazorApp1.Client.Components.SpeechRecognitionComponent).Assembly); 36 | 37 | app.Run(); 38 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/script.module.js: -------------------------------------------------------------------------------- 1 | export const createInstance = (dotnetObjRef) => { 2 | const w = window; 3 | const TSpeechRecognition = w.webkitSpeechRecognition || w.SpeechRecognition; 4 | const speechRecognition = TSpeechRecognition ? new TSpeechRecognition() : null; 5 | const falseFunc = () => false; 6 | const invokeMethodAsync = dotnetObjRef.invokeMethodAsync.bind(dotnetObjRef); 7 | if (!speechRecognition) 8 | return ({ available: falseFunc, start: falseFunc, stop: falseFunc }); 9 | speechRecognition.onresult = (ev) => { 10 | invokeMethodAsync('_OnResult', { 11 | resultIndex: ev.resultIndex, 12 | results: Array.from(ev.results).map(result => ({ 13 | isFinal: result.isFinal, 14 | items: Array.from(result).map(item => ({ 15 | confidence: item.confidence, 16 | transcript: item.transcript 17 | })) 18 | })) 19 | }); 20 | }; 21 | speechRecognition.onend = () => invokeMethodAsync('_OnEnd'); 22 | return ({ 23 | available: () => true, 24 | start: (options) => Object.assign(speechRecognition, options).start(), 25 | stop: () => speechRecognition.stop() 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace SampleSite.Server 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @using SampleSite.Components 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Blazor SpeechSynthesis Sample Site - Server App 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 | An error has occurred. This application may no longer respond until reloaded. 26 | 27 | 28 | An unhandled exception has occurred. See browser dev tools for details. 29 | 30 | Reload 31 | 🗙 32 |
33 | 34 | @**@ 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 | @using System.Runtime.InteropServices 2 | 3 | 10 | 11 | 12 | 13 | 34 | 35 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/SampleSite.Client.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/SampleSite.Client.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/Server/SampleSite.Client.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/SampleSite.Server.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/SampleSite.Server.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/Server/SampleSite.Server.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "dotnet", 14 | "args": [ 15 | "run" 16 | ], 17 | "cwd": "${workspaceFolder}", 18 | "stopAtEntry": false, 19 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 20 | "serverReadyAction": { 21 | "action": "openExternally", 22 | "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" 23 | }, 24 | "env": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "sourceFileMap": { 28 | "/Views": "${workspaceFolder}/Views" 29 | } 30 | }, 31 | { 32 | "name": ".NET Core Attach", 33 | "type": "coreclr", 34 | "request": "attach", 35 | "processId": "${command:pickProcess}" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "dotnet", 14 | "args": [ 15 | "run" 16 | ], 17 | "cwd": "${workspaceFolder}", 18 | "stopAtEntry": false, 19 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 20 | "serverReadyAction": { 21 | "action": "openExternally", 22 | "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" 23 | }, 24 | "env": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "sourceFileMap": { 28 | "/Views": "${workspaceFolder}/Views" 29 | } 30 | }, 31 | { 32 | "name": ".NET Core Attach", 33 | "type": "coreclr", 34 | "request": "attach", 35 | "processId": "${command:pickProcess}" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:50054", 8 | "sslPort": 44303 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 17 | "applicationUrl": "http://localhost:5082", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 27 | "applicationUrl": "https://localhost:7051;http://localhost:5082", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.JSInterop; 3 | using Toolbelt.Blazor.SpeechRecognition; 4 | 5 | namespace Toolbelt.Blazor.Extensions.DependencyInjection; 6 | 7 | /// 8 | /// Extension methods for adding SpeechRecognition service. 9 | /// 10 | public static class SpeechRecognitionExtensions 11 | { 12 | /// 13 | /// Adds a SpeechRecognition service to the specified Microsoft.Extensions.DependencyInjection.IServiceCollection. 14 | /// 15 | /// The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the service to. 16 | public static IServiceCollection AddSpeechRecognition(this IServiceCollection services) => services.AddSpeechRecognition(configure: null); 17 | 18 | /// 19 | /// Adds a SpeechRecognition service to the specified Microsoft.Extensions.DependencyInjection.IServiceCollection. 20 | /// 21 | /// The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the service to. 22 | /// A delegate that is used to configure the SpeechRecognitionOptions. 23 | public static IServiceCollection AddSpeechRecognition(this IServiceCollection services, Action? configure) 24 | { 25 | services.AddTransient(serviceProvider => 26 | { 27 | var jsRuntime = serviceProvider.GetRequiredService(); 28 | var speechRecognitionService = new global::Toolbelt.Blazor.SpeechRecognition.SpeechRecognition(jsRuntime); 29 | configure?.Invoke(speechRecognitionService.Options); 30 | return speechRecognitionService; 31 | }); 32 | return services; 33 | } 34 | } -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/script.module.ts: -------------------------------------------------------------------------------- 1 | type SpeechRecognitionOptions = { 2 | lang: string; 3 | continuous: boolean; 4 | interimResults: boolean; 5 | } 6 | 7 | type SpeechRecognition = { 8 | start: () => void; 9 | stop: () => void; 10 | onresult: (ev: { 11 | resultIndex: number; 12 | results: SpeechRecognitionResultList; 13 | }) => any; 14 | onend: (ev: Event) => any; 15 | } & SpeechRecognitionOptions; 16 | 17 | declare global { 18 | interface Window { 19 | SpeechRecognition: (new () => SpeechRecognition) | undefined, 20 | webkitSpeechRecognition: (new () => SpeechRecognition) | undefined 21 | } 22 | } 23 | 24 | interface DotNetObjectRef { 25 | invokeMethodAsync(methodName: string, ...args: any[]): Promise; 26 | } 27 | 28 | export const createInstance = (dotnetObjRef: DotNetObjectRef) => { 29 | const w = window; 30 | const TSpeechRecognition = w.webkitSpeechRecognition || w.SpeechRecognition; 31 | const speechRecognition = TSpeechRecognition ? new TSpeechRecognition() : null; 32 | const falseFunc = () => false; 33 | const invokeMethodAsync = dotnetObjRef.invokeMethodAsync.bind(dotnetObjRef); 34 | 35 | if (!speechRecognition) return ({ available: falseFunc, start: falseFunc, stop: falseFunc }); 36 | 37 | speechRecognition.onresult = (ev) => { 38 | invokeMethodAsync('_OnResult', { 39 | resultIndex: ev.resultIndex, 40 | results: Array.from(ev.results).map(result => ({ 41 | isFinal: result.isFinal, 42 | items: Array.from(result).map(item => ({ 43 | confidence: item.confidence, 44 | transcript: item.transcript 45 | })) 46 | })) 47 | }); 48 | } 49 | 50 | speechRecognition.onend = () => invokeMethodAsync('_OnEnd'); 51 | 52 | return ({ 53 | available: () => true, 54 | start: (options: SpeechRecognitionOptions) => Object.assign(speechRecognition, options).start(), 55 | stop: () => speechRecognition.stop() 56 | }); 57 | } -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row { 41 | justify-content: space-between; 42 | } 43 | 44 | .top-row ::deep a, .top-row ::deep .btn-link { 45 | margin-left: 0; 46 | } 47 | } 48 | 49 | @media (min-width: 641px) { 50 | .page { 51 | flex-direction: row; 52 | } 53 | 54 | .sidebar { 55 | width: 250px; 56 | height: 100vh; 57 | position: sticky; 58 | top: 0; 59 | } 60 | 61 | .top-row { 62 | position: sticky; 63 | top: 0; 64 | z-index: 1; 65 | } 66 | 67 | .top-row.auth ::deep a:first-child { 68 | flex: 1; 69 | text-align: right; 70 | width: 0; 71 | } 72 | 73 | .top-row, article { 74 | padding-left: 2rem !important; 75 | padding-right: 1.5rem !important; 76 | } 77 | } 78 | 79 | #blazor-error-ui { 80 | background: lightyellow; 81 | bottom: 0; 82 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 83 | display: none; 84 | left: 0; 85 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 86 | position: fixed; 87 | width: 100%; 88 | z-index: 1000; 89 | } 90 | 91 | #blazor-error-ui .dismiss { 92 | cursor: pointer; 93 | position: absolute; 94 | right: 0.75rem; 95 | top: 0.5rem; 96 | } 97 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | a, .btn-link { 6 | color: #006bb7; 7 | } 8 | 9 | .btn-primary { 10 | color: #fff; 11 | background-color: #1b6ec2; 12 | border-color: #1861ac; 13 | } 14 | 15 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 16 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 17 | } 18 | 19 | .content { 20 | padding-top: 1.1rem; 21 | } 22 | 23 | h1:focus { 24 | outline: none; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid #e50000; 33 | } 34 | 35 | .validation-message { 36 | color: #e50000; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | 49 | .darker-border-checkbox.form-check-input { 50 | border-color: #929292; 51 | } 52 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Components/App.razor: -------------------------------------------------------------------------------- 1 | @using Toolbelt.Blazor.SpeechRecognition 2 | @inject SpeechRecognition SpeechRecognition 3 | @implements IDisposable 4 | 5 |
6 | SpeechRecognition API is @(_available ? "available" : "not available"). 7 |
8 | 9 |
10 | 14 |
15 | 16 | 17 | 18 | 19 | 20 |
21 | @foreach (var result in this._results) 22 | { 23 | if (result.IsFinal) 24 | { 25 | @result.Items[0].Transcript 26 | } 27 | else 28 | { 29 | @result.Items[0].Transcript 30 | } 31 | } 32 |
33 | 34 | @code 35 | { 36 | private SpeechRecognitionResult[] _results = Array.Empty(); 37 | 38 | private bool _available = true; 39 | 40 | private bool _isListening = false; 41 | 42 | protected override void OnInitialized() 43 | { 44 | this.SpeechRecognition.Lang = "en-US"; 45 | this.SpeechRecognition.InterimResults = true; 46 | this.SpeechRecognition.Continuous = true; 47 | this.SpeechRecognition.Result += OnSpeechRecognized; 48 | this.SpeechRecognition.End += OnSpeechEnded; 49 | } 50 | 51 | protected override async Task OnAfterRenderAsync(bool firstRender) 52 | { 53 | if (firstRender) 54 | { 55 | this._available = await this.SpeechRecognition.IsAvailableAsync(); 56 | this.StateHasChanged(); 57 | } 58 | } 59 | 60 | private void OnChangeLang(ChangeEventArgs args) 61 | { 62 | this.SpeechRecognition.Lang = args.Value.ToString(); 63 | } 64 | 65 | private void OnSpeechRecognized(object sender, SpeechRecognitionEventArgs args) 66 | { 67 | this._results = args.Results.Skip(args.ResultIndex).ToArray(); 68 | this.StateHasChanged(); 69 | } 70 | 71 | private async Task OnClickStart() 72 | { 73 | if (!this._isListening) 74 | { 75 | this._isListening = true; 76 | await this.SpeechRecognition.StartAsync(); 77 | } 78 | } 79 | 80 | private async Task OnClickStop() 81 | { 82 | if (this._isListening) 83 | { 84 | this._isListening = false; 85 | await this.SpeechRecognition.StopAsync(); 86 | } 87 | } 88 | 89 | private void OnSpeechEnded(object sender, EventArgs args) 90 | { 91 | if (this._isListening) 92 | { 93 | this._isListening = false; 94 | this.StateHasChanged(); 95 | } 96 | } 97 | 98 | public void Dispose() 99 | { 100 | this.SpeechRecognition.Result -= OnSpeechRecognized; 101 | this.SpeechRecognition.End -= OnSpeechEnded; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Client/wwwroot/css/blazor-ui.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: #ffffe0; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, .2); 5 | display: none; 6 | left: 0; 7 | padding: .6rem 1.25rem .7rem 1.25rem; 8 | position: fixed; 9 | right: 0; 10 | z-index: 1000 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: .75rem; 17 | top: .5rem 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | .loading-progress { 31 | position: absolute; 32 | display: block; 33 | width: 8rem; 34 | height: 8rem; 35 | margin: auto; 36 | top: 20vh; 37 | left: 0; 38 | right: 0; 39 | } 40 | 41 | .loading-progress circle { 42 | fill: none; 43 | stroke: #e0e0e0; 44 | stroke-width: 0.6rem; 45 | transform-origin: 50% 50%; 46 | transform: rotate(-90deg); 47 | } 48 | 49 | .loading-progress circle:last-child { 50 | stroke: #1b6ec2; 51 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; 52 | transition: stroke-dasharray 0.05s ease-in-out; 53 | } 54 | 55 | .loading-progress-text { 56 | position: absolute; 57 | text-align: center; 58 | font-weight: bold; 59 | top: calc(20vh + 3.4rem); 60 | left: 0; 61 | right: 0; 62 | } 63 | 64 | .loading-progress-text:after { 65 | content: var(--blazor-load-percentage-text, "Loading"); 66 | } 67 | -------------------------------------------------------------------------------- /SampleSites/SampleSite.Server/wwwroot/css/blazor-ui.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: #ffffe0; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, .2); 5 | display: none; 6 | left: 0; 7 | padding: .6rem 1.25rem .7rem 1.25rem; 8 | position: fixed; 9 | right: 0; 10 | z-index: 1000 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: .75rem; 17 | top: .5rem 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | .loading-progress { 31 | position: absolute; 32 | display: block; 33 | width: 8rem; 34 | height: 8rem; 35 | margin: auto; 36 | top: 20vh; 37 | left: 0; 38 | right: 0; 39 | } 40 | 41 | .loading-progress circle { 42 | fill: none; 43 | stroke: #e0e0e0; 44 | stroke-width: 0.6rem; 45 | transform-origin: 50% 50%; 46 | transform: rotate(-90deg); 47 | } 48 | 49 | .loading-progress circle:last-child { 50 | stroke: #1b6ec2; 51 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; 52 | transition: stroke-dasharray 0.05s ease-in-out; 53 | } 54 | 55 | .loading-progress-text { 56 | position: absolute; 57 | text-align: center; 58 | font-weight: bold; 59 | top: calc(20vh + 3.4rem); 60 | left: 0; 61 | right: 0; 62 | } 63 | 64 | .loading-progress-text:after { 65 | content: var(--blazor-load-percentage-text, "Loading"); 66 | } 67 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1/Components/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | min-width: 1.25rem; 31 | width: 1.25rem; 32 | height: 1.25rem; 33 | margin-right: 0.75rem; 34 | top: -1px; 35 | background-size: cover; 36 | } 37 | 38 | .bi-house-door-fill-nav-menu { 39 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 40 | } 41 | 42 | .bi-plus-square-fill-nav-menu { 43 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='white' viewBox='200 -860 700 700' %3E%3Cpath d='M480-400q-50 0-85-35t-35-85v-240q0-50 35-85t85-35q50 0 85 35t35 85v240q0 50-35 85t-85 35Zm0-240Zm-40 520v-123q-104-14-172-93t-68-184h80q0 83 58.5 141.5T480-320q83 0 141.5-58.5T680-520h80q0 105-68 184t-172 93v123h-80Zm40-360q17 0 28.5-11.5T520-520v-240q0-17-11.5-28.5T480-800q-17 0-28.5 11.5T440-760v240q0 17 11.5 28.5T480-480Z'/%3E%3C/svg%3E"); 44 | } 45 | 46 | .nav-item { 47 | font-size: 0.9rem; 48 | padding-bottom: 0.5rem; 49 | } 50 | 51 | .nav-item:first-of-type { 52 | padding-top: 1rem; 53 | } 54 | 55 | .nav-item:last-of-type { 56 | padding-bottom: 1rem; 57 | } 58 | 59 | .nav-item ::deep .nav-link { 60 | color: #d7d7d7; 61 | background: none; 62 | border: none; 63 | border-radius: 4px; 64 | height: 3rem; 65 | display: flex; 66 | align-items: center; 67 | line-height: 1rem; 68 | width: 100%; 69 | } 70 | 71 | .nav-item ::deep a.active { 72 | background-color: rgba(255,255,255,0.37); 73 | color: white; 74 | } 75 | 76 | .nav-item ::deep .nav-link:hover { 77 | background-color: rgba(255,255,255,0.1); 78 | color: white; 79 | } 80 | 81 | .nav-scrollable { 82 | display: none; 83 | } 84 | 85 | .navbar-toggler:checked ~ .nav-scrollable { 86 | display: block; 87 | } 88 | 89 | @media (min-width: 641px) { 90 | .navbar-toggler { 91 | display: none; 92 | } 93 | 94 | .nav-scrollable { 95 | /* Never collapse the sidebar for wide screens */ 96 | display: block; 97 | /* Allow sidebar to scroll for tall menus */ 98 | height: calc(100vh - 3.5rem); 99 | overflow-y: auto; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /SampleSites/Net8App/BlazorApp1/BlazorApp1.Client/Components/SpeechRecognitionComponent.razor: -------------------------------------------------------------------------------- 1 | @using System.Runtime.InteropServices 2 | @implements IDisposable 3 | @inject SpeechRecognition SpeechRecognition 4 | 5 |
6 | 7 |
8 |
9 |
Running on:
10 |
@RuntimeInformation.ProcessArchitecture
11 |
SpeechRecognition API:
12 |
@(_available ? "Available" : "Not Available")
13 |
14 |
15 | 16 |
17 | 23 |
24 | 25 |
26 | 27 | 30 | 31 | 34 | 35 |
36 | 37 | 38 |
39 | @foreach (var result in this._results) 40 | { 41 | if (result.IsFinal) 42 | { 43 | @result.Items![0].Transcript 44 | } 45 | else 46 | { 47 | @result.Items![0].Transcript 48 | } 49 | } 50 |
51 | 52 |
53 | 54 | @code 55 | { 56 | private IEnumerable _langs = ["en-US", "ja-JP"]; 57 | 58 | private SpeechRecognitionResult[] _results = []; 59 | 60 | private bool _available = true; 61 | 62 | private bool _isListening = false; 63 | 64 | protected override void OnInitialized() 65 | { 66 | this.SpeechRecognition.Lang = "en-US"; 67 | this.SpeechRecognition.InterimResults = true; 68 | this.SpeechRecognition.Continuous = true; 69 | this.SpeechRecognition.Result += OnSpeechRecognized; 70 | this.SpeechRecognition.End += OnSpeechEnded; 71 | } 72 | 73 | protected override async Task OnAfterRenderAsync(bool firstRender) 74 | { 75 | if (firstRender) 76 | { 77 | this._available = await this.SpeechRecognition.IsAvailableAsync(); 78 | this.StateHasChanged(); 79 | } 80 | } 81 | 82 | private void OnChangeLang(ChangeEventArgs args) 83 | { 84 | this.SpeechRecognition.Lang = args.Value?.ToString() ?? "en-US"; 85 | } 86 | 87 | private void OnSpeechRecognized(object? sender, SpeechRecognitionEventArgs args) 88 | { 89 | this._results = args.Results?.Skip(args.ResultIndex).ToArray() ?? []; 90 | this.StateHasChanged(); 91 | } 92 | 93 | private async Task OnClickStart() 94 | { 95 | if (this._isListening) return; 96 | this._isListening = true; 97 | await this.SpeechRecognition.StartAsync(); 98 | } 99 | 100 | private async Task OnClickStop() 101 | { 102 | if (!this._isListening) return; 103 | this._isListening = false; 104 | await this.SpeechRecognition.StopAsync(); 105 | } 106 | 107 | private void OnSpeechEnded(object? sender, EventArgs args) 108 | { 109 | if (this._isListening) 110 | { 111 | this._isListening = false; 112 | this.StateHasChanged(); 113 | } 114 | } 115 | 116 | public void Dispose() 117 | { 118 | this.SpeechRecognition.Result -= OnSpeechRecognized; 119 | this.SpeechRecognition.End -= OnSpeechEnded; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blazor Speech Recognition [![NuGet Package](https://img.shields.io/nuget/v/Toolbelt.Blazor.SpeechRecognition.svg)](https://www.nuget.org/packages/Toolbelt.Blazor.SpeechRecognition/) 2 | 3 | ## Summary 4 | 5 | This is a class library for Blazor app to provide Speech Recognition API access. 6 | 7 | ## Requirements 8 | 9 | [Blazor](https://blazor.net/) v.6.0, 7.0, 8.0 or later. 10 | 11 | Both "Blazor WebAssembly" and "Blazor Server" are supoorted. 12 | 13 | 14 | ## Quick Start 15 | 16 | ### 1. Installation and Registration 17 | 18 | **Step.1-1** Install the library via NuGet package, like this. 19 | 20 | ```shell 21 | > dotnet add package Toolbelt.Blazor.SpeechRecognition 22 | ``` 23 | 24 | **Step.1-2** Register `SpeechRecognition` service into the DI container. 25 | 26 | If the project is a Blazor Server App or a Blazor WebAssembly App ver.3.1 Preview 4 or earlyer, add the code into the `ConfigureService` method in the `Startup` class of your Blazor application. 27 | 28 | ```csharp 29 | // Program.cs 30 | 31 | using Toolbelt.Blazor.Extensions.DependencyInjection; // <- Add this, and... 32 | ... 33 | var builder = ... 34 | ... 35 | builder.Services.AddSpeechRecognition(); // <- Add this line. 36 | ... 37 | ``` 38 | 39 | ### 2. Usage in your Blazor component (.razor) 40 | 41 | **Step.2-1** Open the `Toolbelt.Blazor.SpeechRecognition` namespace, and inject the `SpeechRecognition` service into the component. 42 | 43 | ```csharp 44 | @{/* This is your component .razor */} 45 | @using Toolbelt.Blazor.SpeechRecognition @{/* Add these two lines. */} 46 | @inject SpeechRecognition SpeechRecognition 47 | ... 48 | ``` 49 | 50 | **Step.2-2** Subscribe `Result` event of the SpeechRecognition service to receive the results of speech recognition. 51 | 52 | ```csharp 53 | protected override void OnInitialized() 54 | { 55 | this.SpeechRecognition.Result += OnSpeechRecognized; 56 | } 57 | 58 | private void OnSpeechRecognized(object sender, SpeechRecognitionEventArgs args) 59 | { 60 | // DO SOMETHING... 61 | } 62 | ``` 63 | 64 | **Step.2-3** Invoke `StartAsync()` method of the SpeechRecognition service when you want to start speech recognition. 65 | 66 | ```csharp 67 | private async Task OnClickStart() 68 | { 69 | await this.SpeechRecognition.StartAsync(); 70 | } 71 | ``` 72 | 73 | **Step.2-4** Implement `IDisposable` interface on the component, and unsubscribe `Result` event when the component is disposing. 74 | 75 | ```csharp 76 | ... 77 | @implements IDisposable 78 | ... 79 | @code { 80 | ... 81 | public void Dispose() 82 | { 83 | this.SpeechRecognition.Result -= OnSpeechRecognized; 84 | } 85 | } 86 | ``` 87 | 88 | See also [sample code on the GitHub repository](https://github.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/blob/master/SampleSites/SampleSite.Components/App.razor). 89 | 90 | ### Configuration 91 | 92 | The calling of `services.AddSpeechRecognition()` injects the references of JavaScript file (.js) - which is bundled with this package - into your page automatically. 93 | 94 | If you don't want this behavior, you can disable these automatic injection, please call `services.AddSpeechRecognition()` with configuration action like this: 95 | 96 | ```csharp 97 | services.AddSpeechRecognition(options => 98 | { 99 | // If you don't want automatic injection of js file, add bellow; 100 | options.DisableClientScriptAutoInjection = true; 101 | }); 102 | ``` 103 | 104 | You can inject the helper JavaScript file manually. The URLs is bellow: 105 | 106 | - `_content/Toolbelt.Blazor.SpeechRecognition/script.min.js` 107 | 108 | ## Release Note 109 | 110 | Release notes is [here](https://github.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/blob/master/RELEASE-NOTES.txt). 111 | 112 | ## License 113 | 114 | [Mozilla Public License Version 2.0](https://github.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/blob/master/LICENSE) -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34302.71 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Toolbelt.Blazor.SpeechRecognition", "Toolbelt.Blazor.SpeechRecognition\Toolbelt.Blazor.SpeechRecognition.csproj", "{F4FBD89A-7024-4A8C-A045-2B313295F466}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleSites", "SampleSites", "{8179E9F9-CDA1-45C9-B756-201E199C42EF}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "README", "README", "{AEDFC8CA-4940-4DD9-94B1-CC25AC1D3F63}" 11 | ProjectSection(SolutionItems) = preProject 12 | README.md = README.md 13 | RELEASE-NOTES.txt = RELEASE-NOTES.txt 14 | EndProjectSection 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleSite.Components", "SampleSites\SampleSite.Components\SampleSite.Components.csproj", "{363C0011-36CD-4FDB-8842-D04B364E4B40}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleSite.Client", "SampleSites\SampleSite.Client\SampleSite.Client.csproj", "{64E88DAA-A721-4D58-B80C-7B0262123937}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleSite.Server", "SampleSites\SampleSite.Server\SampleSite.Server.csproj", "{59149CDD-38A1-4D1D-9D1A-07A15465A858}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NET8", "NET8", "{E2622C78-2FE6-4655-95EF-2B4D419A7D50}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorApp1", "SampleSites\Net8App\BlazorApp1\BlazorApp1\BlazorApp1.csproj", "{80EF9BF4-1245-4779-9C6B-7445897AACBD}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorApp1.Client", "SampleSites\Net8App\BlazorApp1\BlazorApp1.Client\BlazorApp1.Client.csproj", "{439B2B76-EF14-4477-A603-D42E91C5586E}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {F4FBD89A-7024-4A8C-A045-2B313295F466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {F4FBD89A-7024-4A8C-A045-2B313295F466}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {F4FBD89A-7024-4A8C-A045-2B313295F466}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {F4FBD89A-7024-4A8C-A045-2B313295F466}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {363C0011-36CD-4FDB-8842-D04B364E4B40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {363C0011-36CD-4FDB-8842-D04B364E4B40}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {363C0011-36CD-4FDB-8842-D04B364E4B40}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {363C0011-36CD-4FDB-8842-D04B364E4B40}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {64E88DAA-A721-4D58-B80C-7B0262123937}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {64E88DAA-A721-4D58-B80C-7B0262123937}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {64E88DAA-A721-4D58-B80C-7B0262123937}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {64E88DAA-A721-4D58-B80C-7B0262123937}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {59149CDD-38A1-4D1D-9D1A-07A15465A858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {59149CDD-38A1-4D1D-9D1A-07A15465A858}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {59149CDD-38A1-4D1D-9D1A-07A15465A858}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {59149CDD-38A1-4D1D-9D1A-07A15465A858}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {80EF9BF4-1245-4779-9C6B-7445897AACBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {80EF9BF4-1245-4779-9C6B-7445897AACBD}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {80EF9BF4-1245-4779-9C6B-7445897AACBD}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {80EF9BF4-1245-4779-9C6B-7445897AACBD}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {439B2B76-EF14-4477-A603-D42E91C5586E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {439B2B76-EF14-4477-A603-D42E91C5586E}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {439B2B76-EF14-4477-A603-D42E91C5586E}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {439B2B76-EF14-4477-A603-D42E91C5586E}.Release|Any CPU.Build.0 = Release|Any CPU 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(NestedProjects) = preSolution 63 | {363C0011-36CD-4FDB-8842-D04B364E4B40} = {8179E9F9-CDA1-45C9-B756-201E199C42EF} 64 | {64E88DAA-A721-4D58-B80C-7B0262123937} = {8179E9F9-CDA1-45C9-B756-201E199C42EF} 65 | {59149CDD-38A1-4D1D-9D1A-07A15465A858} = {8179E9F9-CDA1-45C9-B756-201E199C42EF} 66 | {E2622C78-2FE6-4655-95EF-2B4D419A7D50} = {8179E9F9-CDA1-45C9-B756-201E199C42EF} 67 | {80EF9BF4-1245-4779-9C6B-7445897AACBD} = {E2622C78-2FE6-4655-95EF-2B4D419A7D50} 68 | {439B2B76-EF14-4477-A603-D42E91C5586E} = {E2622C78-2FE6-4655-95EF-2B4D419A7D50} 69 | EndGlobalSection 70 | GlobalSection(ExtensibilityGlobals) = postSolution 71 | SolutionGuid = {4CCF99FF-7770-4E09-B6F9-61BD869AC7A7} 72 | EndGlobalSection 73 | EndGlobal 74 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/Toolbelt.Blazor.SpeechRecognition.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0;net8.0 5 | enable 6 | true 7 | $(WarningsAsErrors);nullable 8 | true 9 | $(PrepareForBuildDependsOn);UpdateILLinkSubstitutionsXml 10 | 5.0 11 | true 12 | 1.0.0 13 | J.Sakamoto 14 | SpeechRecognition API access for your Blazor apps. 15 | Copyright © 2019-2023 J.Sakamoto, Mozilla Public License 2.0 16 | MPL-2.0 17 | https://github.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition 18 | blazor,speech,speechrecognition 19 | true 20 | bin\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).xml 21 | $(NoWarn);1591 22 | ..\_dist\ 23 | (Please write the package release notes in "../RELEASE-NOTES.txt") 24 | nupkg-icon.png 25 | README.md 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ILLink.Descriptors.xml 36 | 37 | 38 | ILLink.Substitutions.xml 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | all 61 | runtime; build; native; contentfiles; analyzers; buildtransitive 62 | 63 | 64 | All 65 | 66 | 67 | 68 | 69 | 70 | false 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | @(ReleaseNoteLines, '%0a');v.0.0 84 | $([System.Text.RegularExpressions.Regex]::Match($(PackageReleaseNotes), "^(v\.[\d\.]+.+?)v\.[\d\.]+", System.Text.RegularExpressions.RegexOptions.Singleline).Groups[1].Value) 85 | 86 | $(PackageReleaseNotes)%0a%0aTo see all the change logs, please visit the following URL.%0a- https://github.com/jsakamoto/Toolbelt.Blazor.SpeechRecognition/blob/master/RELEASE-NOTES.txt 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | _dist/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | 257 | # CodeRush 258 | .cr/ 259 | 260 | # Python Tools for Visual Studio (PTVS) 261 | __pycache__/ 262 | *.pyc -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 5 | "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": ["DOM","ES2020"], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 12 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | // "outDir": "./", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "composite": true, /* Enable project compilation */ 17 | // "incremental": true, /* Enable incremental compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | 58 | /* Experimental Options */ 59 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | 62 | }, 63 | "compileOnSave": true 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Toolbelt.Blazor.SpeechRecognition/SpeechRecognition.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Reflection; 3 | using Microsoft.JSInterop; 4 | 5 | namespace Toolbelt.Blazor.SpeechRecognition; 6 | 7 | /// 8 | /// The service allows us to access the Speech Recognition API of Web browsers. 9 | /// 10 | public class SpeechRecognition : IAsyncDisposable 11 | { 12 | private static readonly string Prefix = "Toolbelt.Blazor.SpeechRecognition."; 13 | 14 | internal readonly SpeechRecognitionOptions Options = new(); 15 | 16 | private readonly IJSRuntime _JSRuntime; 17 | 18 | private IJSObjectReference? _recognizer = null; 19 | 20 | private DotNetObjectReference? _objectRefOfThis; 21 | 22 | /// 23 | /// Occurs when the speech recognition service returns a result of recognition of a word or phrase. 24 | /// 25 | public event EventHandler? Result; 26 | 27 | /// 28 | /// Occurs when the speech recognition service has disconnected. 29 | /// 30 | public event EventHandler? End; 31 | 32 | /// 33 | /// Gets or sets the language of the current SpeechRecognition object. 34 | /// 35 | public string? Lang { get; set; } 36 | 37 | /// 38 | /// Gets or sets a value that indicates whether the speech recognition service should return for each recognition or only a single result. 39 | /// 40 | public bool Continuous { get; set; } 41 | 42 | /// 43 | /// Gets or sets a value that indicates whether the speech recognition service should return interim results. 44 | /// 45 | public bool InterimResults { get; set; } 46 | 47 | /// 48 | /// Initializes a new instance of the class. 49 | /// 50 | /// The instance. 51 | public SpeechRecognition(IJSRuntime jsRuntime) 52 | { 53 | this._JSRuntime = jsRuntime; 54 | } 55 | 56 | private bool _scriptLoaded = false; 57 | 58 | private SemaphoreSlim _syncer = new SemaphoreSlim(1, 1); 59 | 60 | private async ValueTask GetRecognizerAsync() 61 | { 62 | if (this._scriptLoaded) return this._recognizer; 63 | await this._syncer.WaitAsync(); 64 | try 65 | { 66 | if (this._scriptLoaded) return this._recognizer; 67 | 68 | this._objectRefOfThis ??= DotNetObjectReference.Create(this); 69 | 70 | if (!this.Options.DisableClientScriptAutoInjection) 71 | { 72 | var isOnLine = await this._JSRuntime.InvokeAsync("Toolbelt.Blazor.getProperty", "navigator.onLine"); 73 | var scriptPath = "./_content/Toolbelt.Blazor.SpeechRecognition/script.module.min.js"; 74 | if (isOnLine) scriptPath += $"?v={this.GetVersionText()}"; 75 | 76 | await using var module = await this._JSRuntime.InvokeAsync("import", scriptPath); 77 | this._recognizer = await module.InvokeAsync("createInstance", this._objectRefOfThis); 78 | } 79 | else 80 | { 81 | await this._JSRuntime.InvokeVoidAsync("eval", "Toolbelt.Blazor.SpeechRecognition.ready"); 82 | this._recognizer = await this._JSRuntime.InvokeAsync(Prefix + "createInstance", this._objectRefOfThis); 83 | } 84 | } 85 | catch (InvalidOperationException) { } 86 | finally 87 | { 88 | this._scriptLoaded = true; 89 | this._syncer.Release(); 90 | } 91 | 92 | return this._recognizer; 93 | } 94 | 95 | private async ValueTask InvokeRecognizerAsync(Func action) 96 | { 97 | var recognizer = await this.GetRecognizerAsync(); 98 | if (recognizer == null) return; 99 | await action.Invoke(recognizer); 100 | } 101 | 102 | private string GetVersionText() 103 | { 104 | var assembly = this.GetType().Assembly; 105 | var version = assembly 106 | .GetCustomAttribute()? 107 | .InformationalVersion ?? assembly.GetName().Version?.ToString() ?? "0.0.0"; 108 | return version; 109 | } 110 | 111 | /// 112 | /// Gets a value that indicates whether the speech recognition service is available on the current device. 113 | /// 114 | public async ValueTask IsAvailableAsync() 115 | { 116 | var recognizer = await this.GetRecognizerAsync(); 117 | if (recognizer == null) return false; 118 | return await recognizer.InvokeAsync("available"); 119 | } 120 | 121 | /// 122 | /// Starts the speech recognition service listening to incoming audio with intent to recognize grammars associated with the current SpeechRecognition object. 123 | /// 124 | /// 125 | public async ValueTask StartAsync() 126 | { 127 | await this.InvokeRecognizerAsync(r => r.InvokeVoidAsync("start", new SpeechRecognitionStartOptions 128 | { 129 | Lang = this.Lang, 130 | Continuous = this.Continuous, 131 | InterimResults = this.InterimResults, 132 | })); 133 | } 134 | 135 | /// 136 | /// Stops the speech recognition service from listening to incoming audio, and doesn't attempt to return a SpeechRecognitionResult any longer. 137 | /// 138 | public ValueTask StopAsync() => this.InvokeRecognizerAsync(r => r.InvokeVoidAsync("stop")); 139 | 140 | [JSInvokable(nameof(_OnResult)), EditorBrowsable(EditorBrowsableState.Never)] 141 | public void _OnResult(SpeechRecognitionEventArgs args) 142 | { 143 | this.Result?.Invoke(this, args); 144 | } 145 | 146 | [JSInvokable(nameof(_OnEnd)), EditorBrowsable(EditorBrowsableState.Never)] 147 | public void _OnEnd() 148 | { 149 | this.End?.Invoke(this, EventArgs.Empty); 150 | } 151 | 152 | public async ValueTask DisposeAsync() 153 | { 154 | GC.SuppressFinalize(this); 155 | 156 | if (this._recognizer != null) 157 | { 158 | try { await this._recognizer.DisposeAsync(); } 159 | catch (JSDisconnectedException) { } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | --------------------------------------------------------------------------------