diff --git a/.agents/skills/livecodes/language-support/references/languages.md b/.agents/skills/livecodes/language-support/references/languages.md index 2927816918..66a48dcb2f 100644 --- a/.agents/skills/livecodes/language-support/references/languages.md +++ b/.agents/skills/livecodes/language-support/references/languages.md @@ -125,6 +125,7 @@ Enabled via `processors` config: | Objective-C++ | `objcpp-wasm`, `objcpp`, `wasm.mm` | Clang WASM | | Java | `java` | Java runtime | | C# | `csharp-wasm`, `cs`, `cs-wasm`, `wasm.cs` | C# WASM | +| Blazor (Wasm) | `blazor-wasm`, `blazor`, `razor`, `razor-wasm`, `wasm.razor` | Razor components rendered by Blazor (WASM) | | VB.NET | `vb-wasm`, `vb`, `vbnet`, `wasm.vb` | VB.NET WASM | | F# | `fsharp`, `fs`, `fsx` | Fable (F# -> JS) | | F# (Wasm) | `fsharp-wasm`, `fs-wasm`, `wasm.fs` | .NET WASM | diff --git a/README.md b/README.md index f9caeb6746..332177b006 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A [feature-rich](https://livecodes.io/docs/features/), open-source, **client-sid [![LiveCodes: npm version](https://img.shields.io/npm/v/livecodes)](https://www.npmjs.com/package/livecodes) [![LiveCodes: npm downloads](https://img.shields.io/npm/dm/livecodes)](https://www.npmjs.com/package/livecodes) [![LiveCodes: jsdelivr downloads](https://data.jsdelivr.com/v1/package/npm/livecodes/badge?style=rounded)](https://www.jsdelivr.com/package/npm/livecodes) -[![LiveCodes: languages](https://img.shields.io/badge/languages-113-blue)](https://livecodes.io/docs/languages/) +[![LiveCodes: languages](https://img.shields.io/badge/languages-114-blue)](https://livecodes.io/docs/languages/) [![LiveCodes: docs](https://img.shields.io/badge/Documentation-575757?logo=gitbook&logoColor=white)](https://livecodes.io/docs/) [![LiveCodes: llms.txt](https://img.shields.io/badge/llms.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms.txt) [![LiveCodes: llms-full.txt](https://img.shields.io/badge/llms--full.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms-full.txt) diff --git a/docs/docs/languages/blazor-wasm.mdx b/docs/docs/languages/blazor-wasm.mdx new file mode 100644 index 0000000000..9909eeea7f --- /dev/null +++ b/docs/docs/languages/blazor-wasm.mdx @@ -0,0 +1,118 @@ +# Blazor (Wasm) + +[Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) is a framework for building interactive web UIs with C# and Razor, running on .NET WebAssembly. + +In LiveCodes, Razor components are compiled in the browser by the real Razor compiler and Roslyn, and rendered by Blazor WebAssembly. Nothing is sent to a server: the compiler, the .NET runtime and the reference assemblies all run in the page. + +## Usage + +Demo: + + +import LiveCodes from '../../src/components/LiveCodes.tsx'; +export const blazorConfig = { + activeEditor: 'script', + script: { + language: 'blazor-wasm', + content: `

Counter

\n +

Current count: @count

\n +\n +@code { + private int count = 0;\n + private void IncrementCount() => count++; +}`, + }, + style: { + language: 'css', + content: `.btn { + font: inherit; + padding: 0.4em 1.1em; + cursor: pointer; +}`, + }, + mode: 'simple', + editor: 'auto', +}; + + + + +### Communication with JavaScript + +The component runs in the context of the result page. A few helpers are available in the browser global `livecodes.blazorWasm` object: + +- `livecodes.blazorWasm.loaded`: A promise that resolves when the .NET WebAssembly runtime has loaded. The Roslyn and Razor compiler payloads are fetched lazily on the first render, so other helpers should be used after this promise resolves. +- `livecodes.blazorWasm.render`: A function that compiles and renders Razor markup. It takes a string of Razor markup as input and returns a promise that resolves with an object containing the `output`, `error`, and `exitCode` properties. +- `livecodes.blazorWasm.runner`: The underlying [`@live-codes/blazor-wasm`](https://www.npmjs.com/package/@live-codes/blazor-wasm) runner, which also exposes `renderProject` (multi-file projects), `render`, `navigateTo` (for `@page` routing) and `payloads`. +- `livecodes.blazorWasm.routes`: The routes declared by `@page` directives in the rendered component. +- `livecodes.blazorWasm.navigateTo`: A function that drives the host's client-side navigation, for `@page` routing. + + +## Language Info + +### Name + +`blazor-wasm` + +### Aliases / Extensions + +`razor`, `blazor`, `razor-wasm`, `wasm.razor` + +### Editor + +`script` + +## Compiler + +The real Razor compiler and Roslyn, compiled to WebAssembly, together with the .NET WebAssembly runtime and the Blazor WebAssembly host — loaded at run time from the [`@live-codes/blazor-wasm`](https://www.npmjs.com/package/@live-codes/blazor-wasm) package. + +### Version + +.NET 10 + +## Limitations + +### Blazor components only + +Only **Blazor components** (`.razor`) are supported, not Razor Pages or MVC views. MVC-only directives such as `@model`, `@section` and tag helpers neither compile nor run. Use the Blazor equivalents: + +| Razor Pages / MVC | Blazor equivalent | +| ------------------------------------- | ------------------------------------------------------------------------- | +| `@model T` | `[Parameter] public T Model { get; set; }` | +| `@section Name { ... }` | a layout component that renders `@Body` (or a `RenderFragment` parameter) | +| tag helpers (`@addTagHelper`, `asp-`) | Blazor components and plain HTML with `@onclick` / `@bind` | +| `@functions { ... }` | `@code { ... }` | +| `@inherits RazorPage` | `@inherits ComponentBase` (components already derive from `ComponentBase`) | + +`@inject` works, but only for services the host already registers (`NavigationManager`, `IJSRuntime`, ...). `Program.cs` is not editable, so custom services cannot be registered. Razor Pages need the MVC view engine (`ViewContext`, tag-helper activation, sections, model binding, `HttpContext`), which a Blazor WebAssembly host does not include — even compiling a `.cshtml` file would fail at run time. + +### Other limitations + +- The first Razor compile takes a few seconds while the Razor generator warms up. Warm renders are fast. +- The runtime and the reference assemblies are downloaded on the first run, so the first compile is a large download. +- `wwwroot/` files are served as data URLs, so `url(...)` inside CSS and `fetch()` do not resolve; only `src`/`href`/`poster` attributes are rewritten. + +## Example Usage + +```html +

Hello, Blazor!

+ + + +@code { + private int count; + + private void Increment() => count++; +} +``` + +## Starter Template + +https://livecodes.io/?template=blazor-wasm + +## Links + +- [Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) +- [Blazor components](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/) +- [Razor syntax reference](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor) +- [@live-codes/blazor-wasm](https://www.npmjs.com/package/@live-codes/blazor-wasm) diff --git a/docs/docs/languages/csharp-wasm.mdx b/docs/docs/languages/csharp-wasm.mdx index bd51c03f6b..be279cc32b 100644 --- a/docs/docs/languages/csharp-wasm.mdx +++ b/docs/docs/languages/csharp-wasm.mdx @@ -14,17 +14,14 @@ export const csharpConfig = { activeEditor: 'script', script: { language: 'csharp-wasm', - content: `using System; - + content: `using System;\n public class Program { public static void Main() { int[] sortedArray = { 1, 3, 5, 7, 9, 11, 13, 15 }; - int itemToSearch = 7; - - int result = BinarySearch(sortedArray, 0, sortedArray.Length - 1, itemToSearch); - + int itemToSearch = 7;\n + int result = BinarySearch(sortedArray, 0, sortedArray.Length - 1, itemToSearch);\n if (result == -1) { Console.WriteLine("Result: Item not found in the array."); @@ -33,8 +30,7 @@ public class Program { Console.WriteLine($"Result: Item found at index -> {result}"); } - } - + }\n public static int BinarySearch(int[] arr, int left, int right, int item) { if (right >= left) @@ -43,13 +39,11 @@ public class Program if (arr[mid] == item) { return mid; - } - + }\n if (arr[mid] > item) { return BinarySearch(arr, left, mid - 1, item); - } - + }\n return BinarySearch(arr, mid + 1, right, item); } return -1; @@ -97,11 +91,11 @@ Example: ## Compiler -Blazor WebAssembly with .NET WebAssembly runtime. +The .NET WebAssembly runtime with Roslyn, loaded at run time from the [`@live-codes/blazor-wasm`](https://www.npmjs.com/package/@live-codes/blazor-wasm) package — the same bundle that backs the [Blazor (Wasm)](./blazor-wasm.mdx) language. Console programs are compiled and run through the bundle's `RunCode`. ### Version -.NET 9.0 +.NET 10 ## Code Formatting diff --git a/docs/src/components/LanguageSliders.tsx b/docs/src/components/LanguageSliders.tsx index 956e0935b8..aec340f1d8 100644 --- a/docs/src/components/LanguageSliders.tsx +++ b/docs/src/components/LanguageSliders.tsx @@ -99,6 +99,7 @@ export default function Sliders() { { name: 'zig-wasm', title: 'Zig (Wasm)' }, { name: 'java', title: 'Java' }, { name: 'csharp-wasm', title: 'C# (Wasm)' }, + { name: 'blazor-wasm', title: 'Blazor (Wasm)' }, { name: 'vb-wasm', title: 'VB.NET (Wasm)' }, { name: 'fsharp', title: 'F#' }, { name: 'fsharp-wasm', title: 'F# (Wasm)' }, diff --git a/docs/src/components/TemplateList.tsx b/docs/src/components/TemplateList.tsx index 7db8a59c35..31772d078a 100644 --- a/docs/src/components/TemplateList.tsx +++ b/docs/src/components/TemplateList.tsx @@ -57,6 +57,7 @@ const templates = [ { name: 'zig-wasm', title: 'Zig (Wasm) Starter', thumbnail: 'zig.svg' }, { name: 'java', title: 'Java Starter', thumbnail: 'java.svg' }, { name: 'csharp-wasm', title: 'C# (Wasm)', thumbnail: 'csharp.svg' }, + { name: 'blazor-wasm', title: 'Blazor (Wasm) Starter', thumbnail: 'blazor.svg' }, { name: 'vb-wasm', title: 'VB.NET (Wasm)', thumbnail: 'vb.svg' }, { name: 'fsharp', title: 'F# Starter', thumbnail: 'fsharp.svg' }, { name: 'fsharp-wasm', title: 'F# (Wasm) Starter', thumbnail: 'fsharp.svg' }, diff --git a/e2e/specs/starter.spec.ts b/e2e/specs/starter.spec.ts index 5ef88c2b79..8dae79225d 100644 --- a/e2e/specs/starter.spec.ts +++ b/e2e/specs/starter.spec.ts @@ -561,6 +561,31 @@ test.describe('Starter Templates from UI', () => { await expect(getResult().locator('text=You clicked')).toHaveText('You clicked 3 times.'); }); + test('Blazor (Wasm) Starter', async ({ page, getTestUrl }) => { + // the .NET runtime, Roslyn and the Razor compiler (~40 MB) are downloaded on the first run + test.slow(); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=New'); + await app.click('text=Blazor (Wasm) Starter'); + + await waitForEditorFocus(app); + await waitForResultUpdate(); + + // the component renders once the runtime and the Razor compiler have loaded + await expect(getResult().locator('h1')).toHaveText('Hello, Blazor!', { timeout: 280_000 }); + + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + + await expect(getResult().locator('text=You clicked')).toHaveText('You clicked 3 times.'); + }); + test('d3 Starter', async ({ page, getTestUrl, editor }) => { test.slow(); @@ -1308,6 +1333,27 @@ test.describe('Starter Templates from URL', () => { await expect(getResult().locator('text=You clicked')).toHaveText('You clicked 3 times.'); }); + test('Blazor (Wasm) Starter (in URL)', async ({ page, getTestUrl }) => { + // the .NET runtime, Roslyn and the Razor compiler (~40 MB) are downloaded on the first run + test.slow(); + + await page.goto(getTestUrl({ template: 'blazor-wasm' })); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await waitForEditorFocus(app); + await waitForResultUpdate(); + + // the component renders once the runtime and the Razor compiler have loaded + await expect(getResult().locator('h1')).toHaveText('Hello, Blazor!', { timeout: 280_000 }); + + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + + await expect(getResult().locator('text=You clicked')).toHaveText('You clicked 3 times.'); + }); + test('D3 Starter (in URL)', async ({ page, getTestUrl, editor }) => { test.slow(); diff --git a/functions/vendors/templates.js b/functions/vendors/templates.js index 2313e9b4ca..3f6a851976 100644 --- a/functions/vendors/templates.js +++ b/functions/vendors/templates.js @@ -55,6 +55,7 @@ export const starterTemplates = { "zig-wasm": "Zig (Wasm) Starter", "java": "Java Starter", "csharp-wasm": "C# (Wasm) Starter", + "blazor-wasm": "Blazor (Wasm) Starter", "vb-wasm": "VB.NET (Wasm) Starter", "fsharp": "F# Starter", "fsharp-wasm": "F# (Wasm) Starter", diff --git a/scripts/build.js b/scripts/build.js index a08e3f1385..53bc4f1d29 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -270,6 +270,7 @@ const iifeBuild = () => 'languages/swift-wasm/lang-swift-wasm-script.ts', 'languages/ada-wasm/lang-ada-wasm-script.ts', 'languages/assembly-wasm/lang-assembly-wasm-script.ts', + 'languages/blazor-wasm/lang-blazor-wasm-script.ts', 'languages/sql/lang-sql-script.ts', 'languages/svelte/lang-svelte-compiler.ts', 'languages/tcl/lang-tcl-script.ts', diff --git a/server/php/inc/starter-templates.json b/server/php/inc/starter-templates.json index 058e77398d..5b465e2502 100644 --- a/server/php/inc/starter-templates.json +++ b/server/php/inc/starter-templates.json @@ -55,6 +55,7 @@ "zig-wasm": "Zig (Wasm) Starter", "java": "Java Starter", "csharp-wasm": "C# (Wasm) Starter", + "blazor-wasm": "Blazor (Wasm) Starter", "vb-wasm": "VB.NET (Wasm) Starter", "fsharp": "F# Starter", "fsharp-wasm": "F# (Wasm) Starter", diff --git a/src/livecodes/UI/command-menu-actions.ts b/src/livecodes/UI/command-menu-actions.ts index 1ea5817699..62a0f18e71 100644 --- a/src/livecodes/UI/command-menu-actions.ts +++ b/src/livecodes/UI/command-menu-actions.ts @@ -311,6 +311,7 @@ export const getCommandMenuActions = ({ 'zig-wasm', 'java', 'csharp-wasm', + 'blazor-wasm', 'vb-wasm', 'fsharp', 'fsharp-wasm', diff --git a/src/livecodes/assets/templates/blazor.svg b/src/livecodes/assets/templates/blazor.svg new file mode 100644 index 0000000000..2f5a30cfe0 --- /dev/null +++ b/src/livecodes/assets/templates/blazor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/livecodes/html/language-info.html b/src/livecodes/html/language-info.html index 39874d1926..e18804b742 100644 --- a/src/livecodes/html/language-info.html +++ b/src/livecodes/html/language-info.html @@ -190,105 +190,79 @@

BBCode

-
-

Blockly

-
- A JavaScript library for building visual programming editors. +
+

Blazor (Wasm)

+
+ Razor components compiled by the real + Razor compiler and + Roslyn, and + rendered by + Blazor WebAssembly + on the .NET WebAssembly runtime — all in the browser, with no server.
-
-
-

Civet

-
- Civet is a programming language that compiles to TypeScript or JavaScript, so you can use - existing tooling but enable concise and powerful syntax. -
-
-
-

Clio

-
- Clio is a fast, distributed, functional programming language that compiles to JavaScript. +
+

Blockly

+
+ A JavaScript library for building visual programming editors.
-
-
-

C++

-
- C++ support using JSCPP (a simple C++ interpreter written in JavaScript). -
-
- It is not a complete implementation of C++. Please refer to - JSCPP documentation - for details. -
-
-
-

C++ (Wasm)

-
- C++ compiled to WebAssembly by - Clang, running entirely in - the browser. This provides a real C++ compiler, unlike the - JSCPP-based C++ - interpreter. +
+

Civet

+
+ Civet is a programming language that compiles to TypeScript or JavaScript, so you can use + existing tooling but enable concise and powerful syntax.
-
-
-

Objective-C (Wasm)

-
- Objective-C compiled to WebAssembly by - Clang, using the - GNUstep - Objective-C runtime. This is a runtime and not a class library, so Foundation is not available. +
+

Clio

+
+ Clio is a fast, distributed, functional programming language that compiles to JavaScript.
-
-
-

Objective-C++ (Wasm)

-
- Objective-C++ compiled to WebAssembly by - Clang, using the - GNUstep - Objective-C runtime. This is a runtime and not a class library, so Foundation is not available. -
- -

ClojureScript (Cherry)

@@ -561,6 +474,70 @@

Common Lisp

+
+

C++

+
+ C++ support using JSCPP (a simple C++ interpreter written in JavaScript). +
+
+ It is not a complete implementation of C++. Please refer to + JSCPP documentation + for details. +
+ +
+
+

C++ (Wasm)

+
+ C++ compiled to WebAssembly by + Clang, running entirely in + the browser. This provides a real C++ compiler, unlike the + JSCPP-based C++ + interpreter. +
+ +

C# (Wasm)

@@ -723,6 +700,36 @@

Eta

+
+

Fanak

+
+ Fanak is a functional programming language that compiles to CIL (.NET). Its compiler and .NET + WebAssembly runtime run entirely in the browser, fetched from the Fanak project, so the first + run downloads the runtime and can take a moment. +
+ +

Fennel

@@ -817,36 +824,6 @@

F# (Wasm)

-
-

Fanak

-
- Fanak is a functional programming language that compiles to CIL (.NET). Its compiler and .NET - WebAssembly runtime run entirely in the browser, fetched from the Fanak project, so the first - run downloads the runtime and can take a moment. -
- -

Gleam

@@ -1438,32 +1415,99 @@

Mustache

>
  • - JavaScript implementationJavaScript implementation +
  • +
  • + LiveCodes Documentations +
  • + +
    +
    +

    Nunjucks

    +
    + A rich and powerful templating language for JavaScript. Nunjucks is essentially a port of + jinja2 +
    + +
    +
    +

    Objective-C (Wasm)

    +
    + Objective-C compiled to WebAssembly by + Clang, using the + GNUstep + Objective-C runtime. This is a runtime and not a class library, so Foundation is not available. +
    + +
    +
    +

    Objective-C++ (Wasm)

    +
    + Objective-C++ compiled to WebAssembly by + Clang, using the + GNUstep + Objective-C runtime. This is a runtime and not a class library, so Foundation is not available. +
    + -
    -
    -

    Nunjucks

    -
    - A rich and powerful templating language for JavaScript. Nunjucks is essentially a port of - jinja2 -
    - @@ -1556,37 +1600,6 @@

    Perl

    -
    -

    PostgreSQL

    -
    PostgreSQL packaged as WASM using PGlite
    - -

    PHP

    PHP running in the browser using Uniter.
    @@ -1649,6 +1662,37 @@

    PHP (Wasm)

    +
    +

    PostgreSQL

    +
    PostgreSQL packaged as WASM using PGlite
    + +

    Tau Prolog

    An open source Prolog interpreter in JavaScript.
    @@ -1699,65 +1743,65 @@

    Pug

    -
    -

    Python (Wasm)

    -
    - Python with the scientific stack, compiled to WebAssembly using Pyodide. -
    -
    - Pyodide allows using Python scientific stack including NumPy, Pandas, Matplotlib, SciPy, - scikit-learn and many more. In addition it’s possible to install pure Python wheels from PyPi. -
    -