[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-dotnet-use-js-interop":3,"mdc--qxcr9z-key":37,"related-repo-dotnet-use-js-interop":3517,"related-org-dotnet-use-js-interop":3625},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":32,"sourceUrl":35,"mdContent":36},"use-js-interop","implement JavaScript interop in Blazor components","Add, review, or fix JavaScript interop in Blazor components. USE FOR: calling JavaScript from Blazor, calling .NET from JavaScript, collocated .razor.js modules, IJSRuntime, IJSObjectReference lifecycle, DotNetObjectReference, ElementReference, timing rules for when JS is available, IAsyncDisposable disposal of JS references, server-side JS interop safety. DO NOT USE FOR: general Blazor component authoring without JS interop needs (use author-component), forms (use collect-user-input).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"dotnet",".NET (Microsoft)","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fdotnet.png",[12,16,19,22],{"name":13,"slug":14,"type":15},".NET","net","tag",{"name":17,"slug":18,"type":15},"JavaScript","javascript",{"name":20,"slug":21,"type":15},"Blazor","blazor",{"name":23,"slug":24,"type":15},"Frontend","frontend",4576,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills","2026-07-12T08:23:04.148289","MIT",332,[31],"agent-skills",{"repoUrl":26,"stars":25,"forks":29,"topics":33,"description":34},[31],"Repository for skills to assist AI coding agents with .NET and C#","https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fdotnet-blazor\u002Fskills\u002Fuse-js-interop","---\nlicense: MIT\nname: use-js-interop\ndescription: >\n  Add, review, or fix JavaScript interop in Blazor components.\n  USE FOR: calling JavaScript from Blazor, calling .NET from JavaScript,\n  collocated .razor.js modules, IJSRuntime, IJSObjectReference lifecycle,\n  DotNetObjectReference, ElementReference, timing rules for when JS is available,\n  IAsyncDisposable disposal of JS references, server-side JS interop safety.\n  DO NOT USE FOR: general Blazor component authoring without JS interop needs\n  (use author-component), forms (use collect-user-input).\n---\n\n# JS Interop in Blazor\n\n## 1. Collocated JS Modules\n\nAlways use collocated `.razor.js` files with `export` — never global `window.*` functions or `\u003Cscript>` tags.\n\n```javascript\n\u002F\u002F ChartPanel.razor.js — placed next to ChartPanel.razor\nexport function initialize(canvas, dotNetRef) { \u002F* ... *\u002F }\nexport function updateData(points) { \u002F* ... *\u002F }\nexport function dispose() { \u002F* ... *\u002F }\n```\n\nImport paths: same project = `\".\u002FComponents\u002FChartPanel.razor.js\"`, RCL = `\".\u002F_content\u002F{AssemblyName}\u002F...\"`.\n\n## 2. Lifecycle Timing\n\n**All JS interop must happen in `OnAfterRenderAsync` or event handlers** — never in `OnInitialized`, `OnParametersSet`, or constructors. JS is not available during server prerendering.\n\nUse a typed interop wrapper (see Section 4) — never call `InvokeAsync`\u002F`InvokeVoidAsync` with raw string literals:\n\n```csharp\nprivate ChartInterop? _chart;\n\nprotected override async Task OnAfterRenderAsync(bool firstRender)\n{\n    if (firstRender)\n    {\n        _chart = new ChartInterop(JS);\n        await _chart.InitializeAsync(_canvasRef);\n    }\n}\n```\n\n**Parameter changes**: set a flag in `OnParametersSet`, apply in `OnAfterRenderAsync`:\n\n```csharp\nprivate bool _dataChanged;\n\nprotected override void OnParametersSet() => _dataChanged = true;\n\nprotected override async Task OnAfterRenderAsync(bool firstRender)\n{\n    if (firstRender) { \u002F* init *\u002F }\n    else if (_dataChanged && _chart is not null)\n    {\n        _dataChanged = false;\n        await _chart.UpdateDataAsync(DataPoints);\n    }\n}\n```\n\n## 3. Batch Related Operations\n\nEach JS interop call crosses the .NET-to-JS boundary (and in Blazor Server, the SignalR circuit). Batching applies in **both directions** — .NET→JS and JS→.NET.\n\n### .NET → JS: merge consecutive calls\n\nIf the C# side makes two or more JS calls in a row, combine them into one JS function:\n\n```csharp\n\u002F\u002F ❌ Two round-trips — theme and locale are always applied together\nawait _module.InvokeVoidAsync(\"applyTheme\", theme);\nawait _module.InvokeVoidAsync(\"applyLocale\", locale);\n\n\u002F\u002F ❌ Result of one call feeds into another — both can stay in JS\nvar token = await _module.InvokeAsync\u003Cstring>(\"createAccessToken\");\nawait _module.InvokeVoidAsync(\"storeToken\", token);\n```\n\n```javascript\n\u002F\u002F ✅ One call applies both — no data dependency, no reason for two trips\nexport function applyPreferences(theme, locale) {\n    document.documentElement.dataset.theme = theme;\n    document.documentElement.lang = locale;\n}\n\n\u002F\u002F ✅ Chain stays in JS — the token never needs to cross the boundary\nexport function createAndStoreToken() {\n    const token = crypto.randomUUID();\n    sessionStorage.setItem('access-token', token);\n    return token;\n}\n```\n\n### JS → .NET: batch callbacks\n\nWhen JS needs to send multiple pieces of data back to .NET, send them in a single `invokeMethodAsync` call rather than making separate callbacks:\n\n```javascript\n\u002F\u002F ❌ Two .NET round-trips from JS\nawait dotNetRef.invokeMethodAsync(ON_VOLUME_CHANGED, volume);\nawait dotNetRef.invokeMethodAsync(ON_PLAYBACK_CHANGED, isPlaying);\n\n\u002F\u002F ✅ One callback with all data\nawait dotNetRef.invokeMethodAsync(ON_PLAYER_STATE_CHANGED, { volume, isPlaying });\n```\n\n**Rule**: if two interop calls always happen together from either side, merge them into one function.\n\n## 4. Typed Interop Wrapper\n\nEncapsulate interop for a feature in a plain class that owns the module lifecycle:\n\n```csharp\npublic sealed class ChartInterop : IAsyncDisposable\n{\n    internal const string ModulePath = \".\u002FComponents\u002FChartPanel.razor.js\";\n    internal const string InitMethod = \"initialize\";\n    internal const string UpdateMethod = \"updateData\";\n    internal const string DisposeMethod = \"dispose\";\n\n    private readonly IJSRuntime _js;\n    private IJSObjectReference? _module;\n\n    public ChartInterop(IJSRuntime js) => _js = js;\n\n    private async ValueTask\u003CIJSObjectReference> GetModuleAsync()\n        => _module ??= await _js.InvokeAsync\u003CIJSObjectReference>(\"import\", ModulePath);\n\n    public async ValueTask InitializeAsync(ElementReference canvas)\n    {\n        var module = await GetModuleAsync();\n        await module.InvokeVoidAsync(InitMethod, canvas);\n    }\n\n    public async ValueTask UpdateDataAsync(IReadOnlyList\u003CDataPoint> points)\n    {\n        var module = await GetModuleAsync();\n        await module.InvokeVoidAsync(UpdateMethod, points);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        try\n        {\n            if (_module is not null)\n            {\n                await _module.InvokeVoidAsync(DisposeMethod);\n                await _module.DisposeAsync();\n            }\n        }\n        catch (JSDisconnectedException) { }\n    }\n}\n```\n\nThe component creates and uses the wrapper with no magic strings:\n\n```razor\n@inject IJSRuntime JS\n@implements IAsyncDisposable\n\n\u003Ccanvas @ref=\"_canvasRef\" width=\"600\" height=\"400\">\u003C\u002Fcanvas>\n\n@code {\n    private ElementReference _canvasRef;\n    private ChartInterop? _chart;\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (firstRender)\n        {\n            _chart = new ChartInterop(JS);\n            await _chart.InitializeAsync(_canvasRef);\n        }\n    }\n\n    async ValueTask IAsyncDisposable.DisposeAsync()\n    {\n        if (_chart is not null)\n            await _chart.DisposeAsync();\n    }\n}\n```\n\nPrefer a concrete class over interface + implementation for interop wrappers. For unit testing, substitute `IJSRuntime` directly (it is already an interface).\n\n## 5. DotNetObjectReference for JS-to-.NET Callbacks\n\n```csharp\n_dotNetRef = DotNetObjectReference.Create(this);\nawait _module.InvokeVoidAsync(\"initialize\", _dotNetRef);\n```\n\nOn the JS side, wrap the `dotNetRef` in a class. Use `async`\u002F`await` with `try\u002Fcatch` (not `.catch()`) to guard against circuit loss. Define .NET method name constants at the top:\n\n```javascript\nconst ON_CLIPBOARD_CHANGED = 'OnClipboardChanged';\n\nclass ClipboardMonitor {\n    #dotNetRef;\n    #abortController;\n\n    constructor(dotNetRef) {\n        this.#dotNetRef = dotNetRef;\n        this.#abortController = new AbortController();\n    }\n\n    start() {\n        document.addEventListener('copy', async () => {\n            try {\n                const text = await navigator.clipboard.readText();\n                await this.#dotNetRef.invokeMethodAsync(ON_CLIPBOARD_CHANGED, text);\n            } catch { \u002F* circuit disconnected or clipboard denied *\u002F }\n        }, { signal: this.#abortController.signal });\n    }\n\n    dispose() {\n        this.#abortController.abort();\n    }\n}\n\nlet monitor;\nexport function initialize(dotNetRef) {\n    monitor = new ClipboardMonitor(dotNetRef);\n    monitor.start();\n}\n\nexport function dispose() {\n    monitor?.dispose();\n}\n```\n\nRules:\n- `[JSInvokable]` methods **must be `public`** — private\u002Finternal silently fails at runtime\n- Wrap `StateHasChanged` in `InvokeAsync` inside `[JSInvokable]` callbacks:\n  ```csharp\n  [JSInvokable]\n  public async Task OnClipboardChanged(string text)\n  {\n      await InvokeAsync(() => { _lastClipboard = text; StateHasChanged(); });\n  }\n  ```\n- Always `try\u002Fcatch` around `invokeMethodAsync` in JS — circuit loss throws\n- Use `const` for .NET method name strings in JS — prevents typo bugs that silently fail\n- Dispose `DotNetObjectReference` in `DisposeAsync`\n\n## 6. Disposal and Server Safety\n\nAlways implement `IAsyncDisposable`. Call JS cleanup first, then dispose references. Catch `JSDisconnectedException` for Blazor Server circuit loss:\n\n```csharp\npublic async ValueTask DisposeAsync()\n{\n    try\n    {\n        if (_module is not null)\n        {\n            await _module.InvokeVoidAsync(\"dispose\");\n            await _module.DisposeAsync();\n        }\n    }\n    catch (JSDisconnectedException) { }\n\n    _dotNetRef?.Dispose();\n}\n```\n\nNever use sync `IDisposable` for JS interop cleanup — `InvokeVoidAsync` returns `ValueTask` and must be awaited.\n\n## 7. ElementReference\n\nPass DOM elements via `@ref`, not string IDs:\n\n```razor\n\u003Ccanvas @ref=\"_canvasRef\" width=\"600\" height=\"400\">\u003C\u002Fcanvas>\n```\n\n```csharp\nawait _chart.InitializeAsync(_canvasRef);\n```\n\n## Checklist\n\n- [ ] JS is in collocated `.razor.js` with `export` — no `window.*` globals\n- [ ] All interop in `OnAfterRenderAsync` or event handlers — never during prerender\n- [ ] `IAsyncDisposable` catches `JSDisconnectedException`\n- [ ] `DotNetObjectReference` disposed in `DisposeAsync`; JS side has `try\u002Fcatch` around `invokeMethodAsync`\n- [ ] `[JSInvokable]` methods are `public` and use `await InvokeAsync(StateHasChanged)`\n- [ ] `InvokeVoidAsync` used when no return value is needed\n- [ ] `ElementReference` instead of string IDs\n- [ ] Related operations batched into single interop calls (both .NET→JS and JS→.NET)\n\n## Common Mistakes Checklist\n\n| Mistake | Fix |\n|---------|-----|\n| Using JS for something achievable with CSS | Use CSS custom properties, `data-` attributes, pseudo-classes |\n| Many fine-grained interop calls | Batch into coarse functions — both .NET→JS and JS→.NET |\n| Component imports JS module directly | Encapsulate in a strongly typed interop class |\n| Magic strings for method names \u002F module paths | Define `internal const` fields in the interop class |\n| Interface + implementation for interop wrapper | Use a plain class; mock `IJSRuntime` for tests instead |\n| JS calls in `OnInitializedAsync` | Move to `OnAfterRenderAsync(firstRender)` |\n| `InvokeAsync\u003Cobject>` for void calls | Use `InvokeVoidAsync` |\n| `IDisposable` with fire-and-forget JS | Use `IAsyncDisposable` with `await` |\n| Global `window.*` JS functions | Use collocated `.razor.js` with `export` |\n| String element IDs passed to JS | Use `ElementReference` with `@ref` |\n| `[JSInvokable]` on private method | Must be `public` — silently fails otherwise |\n| `DotNetObjectReference` not disposed | Dispose in `DisposeAsync` — causes memory leak |\n| `StateHasChanged()` without `InvokeAsync` | Wrap in `await InvokeAsync(() => { StateHasChanged(); })` |\n| JS `invokeMethodAsync` without error handling | Wrap in `try\u002Fcatch` — circuit loss throws |\n| Bare `dotNetRef` in JS event handlers | Wrap in a class with `#dotNetRef` private field |\n| Magic strings in JS `invokeMethodAsync` calls | Use `const` at module top — typos silently fail at runtime |\n| JS calls in `OnParametersSetAsync` | Track changes, apply in `OnAfterRenderAsync` with guard |\n| No null check before calling module | Check `module is not null` before use |\n",{"data":38,"body":39},{"license":28,"name":4,"description":6},{"type":40,"children":41},"root",[42,51,58,97,258,279,285,320,341,437,461,568,574,586,593,598,660,965,971,984,1148,1158,1164,1169,1508,1513,1703,1716,1722,1745,1788,2505,2510,2660,2666,2687,2799,2827,2833,2846,2859,2873,2879,3054,3060,3511],{"type":43,"tag":44,"props":45,"children":47},"element","h1",{"id":46},"js-interop-in-blazor",[48],{"type":49,"value":50},"text","JS Interop in Blazor",{"type":43,"tag":52,"props":53,"children":55},"h2",{"id":54},"_1-collocated-js-modules",[56],{"type":49,"value":57},"1. Collocated JS Modules",{"type":43,"tag":59,"props":60,"children":61},"p",{},[62,64,71,73,79,81,87,89,95],{"type":49,"value":63},"Always use collocated ",{"type":43,"tag":65,"props":66,"children":68},"code",{"className":67},[],[69],{"type":49,"value":70},".razor.js",{"type":49,"value":72}," files with ",{"type":43,"tag":65,"props":74,"children":76},{"className":75},[],[77],{"type":49,"value":78},"export",{"type":49,"value":80}," — never global ",{"type":43,"tag":65,"props":82,"children":84},{"className":83},[],[85],{"type":49,"value":86},"window.*",{"type":49,"value":88}," functions or ",{"type":43,"tag":65,"props":90,"children":92},{"className":91},[],[93],{"type":49,"value":94},"\u003Cscript>",{"type":49,"value":96}," tags.",{"type":43,"tag":98,"props":99,"children":103},"pre",{"className":100,"code":101,"language":18,"meta":102,"style":102},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F ChartPanel.razor.js — placed next to ChartPanel.razor\nexport function initialize(canvas, dotNetRef) { \u002F* ... *\u002F }\nexport function updateData(points) { \u002F* ... *\u002F }\nexport function dispose() { \u002F* ... *\u002F }\n","",[104],{"type":43,"tag":65,"props":105,"children":106},{"__ignoreMap":102},[107,119,182,224],{"type":43,"tag":108,"props":109,"children":112},"span",{"class":110,"line":111},"line",1,[113],{"type":43,"tag":108,"props":114,"children":116},{"style":115},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[117],{"type":49,"value":118},"\u002F\u002F ChartPanel.razor.js — placed next to ChartPanel.razor\n",{"type":43,"tag":108,"props":120,"children":122},{"class":110,"line":121},2,[123,128,134,140,146,152,157,162,167,172,177],{"type":43,"tag":108,"props":124,"children":126},{"style":125},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[127],{"type":49,"value":78},{"type":43,"tag":108,"props":129,"children":131},{"style":130},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[132],{"type":49,"value":133}," function",{"type":43,"tag":108,"props":135,"children":137},{"style":136},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[138],{"type":49,"value":139}," initialize",{"type":43,"tag":108,"props":141,"children":143},{"style":142},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[144],{"type":49,"value":145},"(",{"type":43,"tag":108,"props":147,"children":149},{"style":148},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic",[150],{"type":49,"value":151},"canvas",{"type":43,"tag":108,"props":153,"children":154},{"style":142},[155],{"type":49,"value":156},",",{"type":43,"tag":108,"props":158,"children":159},{"style":148},[160],{"type":49,"value":161}," dotNetRef",{"type":43,"tag":108,"props":163,"children":164},{"style":142},[165],{"type":49,"value":166},")",{"type":43,"tag":108,"props":168,"children":169},{"style":142},[170],{"type":49,"value":171}," {",{"type":43,"tag":108,"props":173,"children":174},{"style":115},[175],{"type":49,"value":176}," \u002F* ... *\u002F",{"type":43,"tag":108,"props":178,"children":179},{"style":142},[180],{"type":49,"value":181}," }\n",{"type":43,"tag":108,"props":183,"children":185},{"class":110,"line":184},3,[186,190,194,199,203,208,212,216,220],{"type":43,"tag":108,"props":187,"children":188},{"style":125},[189],{"type":49,"value":78},{"type":43,"tag":108,"props":191,"children":192},{"style":130},[193],{"type":49,"value":133},{"type":43,"tag":108,"props":195,"children":196},{"style":136},[197],{"type":49,"value":198}," updateData",{"type":43,"tag":108,"props":200,"children":201},{"style":142},[202],{"type":49,"value":145},{"type":43,"tag":108,"props":204,"children":205},{"style":148},[206],{"type":49,"value":207},"points",{"type":43,"tag":108,"props":209,"children":210},{"style":142},[211],{"type":49,"value":166},{"type":43,"tag":108,"props":213,"children":214},{"style":142},[215],{"type":49,"value":171},{"type":43,"tag":108,"props":217,"children":218},{"style":115},[219],{"type":49,"value":176},{"type":43,"tag":108,"props":221,"children":222},{"style":142},[223],{"type":49,"value":181},{"type":43,"tag":108,"props":225,"children":227},{"class":110,"line":226},4,[228,232,236,241,246,250,254],{"type":43,"tag":108,"props":229,"children":230},{"style":125},[231],{"type":49,"value":78},{"type":43,"tag":108,"props":233,"children":234},{"style":130},[235],{"type":49,"value":133},{"type":43,"tag":108,"props":237,"children":238},{"style":136},[239],{"type":49,"value":240}," dispose",{"type":43,"tag":108,"props":242,"children":243},{"style":142},[244],{"type":49,"value":245},"()",{"type":43,"tag":108,"props":247,"children":248},{"style":142},[249],{"type":49,"value":171},{"type":43,"tag":108,"props":251,"children":252},{"style":115},[253],{"type":49,"value":176},{"type":43,"tag":108,"props":255,"children":256},{"style":142},[257],{"type":49,"value":181},{"type":43,"tag":59,"props":259,"children":260},{},[261,263,269,271,277],{"type":49,"value":262},"Import paths: same project = ",{"type":43,"tag":65,"props":264,"children":266},{"className":265},[],[267],{"type":49,"value":268},"\".\u002FComponents\u002FChartPanel.razor.js\"",{"type":49,"value":270},", RCL = ",{"type":43,"tag":65,"props":272,"children":274},{"className":273},[],[275],{"type":49,"value":276},"\".\u002F_content\u002F{AssemblyName}\u002F...\"",{"type":49,"value":278},".",{"type":43,"tag":52,"props":280,"children":282},{"id":281},"_2-lifecycle-timing",[283],{"type":49,"value":284},"2. Lifecycle Timing",{"type":43,"tag":59,"props":286,"children":287},{},[288,302,304,310,312,318],{"type":43,"tag":289,"props":290,"children":291},"strong",{},[292,294,300],{"type":49,"value":293},"All JS interop must happen in ",{"type":43,"tag":65,"props":295,"children":297},{"className":296},[],[298],{"type":49,"value":299},"OnAfterRenderAsync",{"type":49,"value":301}," or event handlers",{"type":49,"value":303}," — never in ",{"type":43,"tag":65,"props":305,"children":307},{"className":306},[],[308],{"type":49,"value":309},"OnInitialized",{"type":49,"value":311},", ",{"type":43,"tag":65,"props":313,"children":315},{"className":314},[],[316],{"type":49,"value":317},"OnParametersSet",{"type":49,"value":319},", or constructors. JS is not available during server prerendering.",{"type":43,"tag":59,"props":321,"children":322},{},[323,325,331,333,339],{"type":49,"value":324},"Use a typed interop wrapper (see Section 4) — never call ",{"type":43,"tag":65,"props":326,"children":328},{"className":327},[],[329],{"type":49,"value":330},"InvokeAsync",{"type":49,"value":332},"\u002F",{"type":43,"tag":65,"props":334,"children":336},{"className":335},[],[337],{"type":49,"value":338},"InvokeVoidAsync",{"type":49,"value":340}," with raw string literals:",{"type":43,"tag":98,"props":342,"children":346},{"className":343,"code":344,"language":345,"meta":102,"style":102},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","private ChartInterop? _chart;\n\nprotected override async Task OnAfterRenderAsync(bool firstRender)\n{\n    if (firstRender)\n    {\n        _chart = new ChartInterop(JS);\n        await _chart.InitializeAsync(_canvasRef);\n    }\n}\n","csharp",[347],{"type":43,"tag":65,"props":348,"children":349},{"__ignoreMap":102},[350,358,367,375,383,392,401,410,419,428],{"type":43,"tag":108,"props":351,"children":352},{"class":110,"line":111},[353],{"type":43,"tag":108,"props":354,"children":355},{},[356],{"type":49,"value":357},"private ChartInterop? _chart;\n",{"type":43,"tag":108,"props":359,"children":360},{"class":110,"line":121},[361],{"type":43,"tag":108,"props":362,"children":364},{"emptyLinePlaceholder":363},true,[365],{"type":49,"value":366},"\n",{"type":43,"tag":108,"props":368,"children":369},{"class":110,"line":184},[370],{"type":43,"tag":108,"props":371,"children":372},{},[373],{"type":49,"value":374},"protected override async Task OnAfterRenderAsync(bool firstRender)\n",{"type":43,"tag":108,"props":376,"children":377},{"class":110,"line":226},[378],{"type":43,"tag":108,"props":379,"children":380},{},[381],{"type":49,"value":382},"{\n",{"type":43,"tag":108,"props":384,"children":386},{"class":110,"line":385},5,[387],{"type":43,"tag":108,"props":388,"children":389},{},[390],{"type":49,"value":391},"    if (firstRender)\n",{"type":43,"tag":108,"props":393,"children":395},{"class":110,"line":394},6,[396],{"type":43,"tag":108,"props":397,"children":398},{},[399],{"type":49,"value":400},"    {\n",{"type":43,"tag":108,"props":402,"children":404},{"class":110,"line":403},7,[405],{"type":43,"tag":108,"props":406,"children":407},{},[408],{"type":49,"value":409},"        _chart = new ChartInterop(JS);\n",{"type":43,"tag":108,"props":411,"children":413},{"class":110,"line":412},8,[414],{"type":43,"tag":108,"props":415,"children":416},{},[417],{"type":49,"value":418},"        await _chart.InitializeAsync(_canvasRef);\n",{"type":43,"tag":108,"props":420,"children":422},{"class":110,"line":421},9,[423],{"type":43,"tag":108,"props":424,"children":425},{},[426],{"type":49,"value":427},"    }\n",{"type":43,"tag":108,"props":429,"children":431},{"class":110,"line":430},10,[432],{"type":43,"tag":108,"props":433,"children":434},{},[435],{"type":49,"value":436},"}\n",{"type":43,"tag":59,"props":438,"children":439},{},[440,445,447,452,454,459],{"type":43,"tag":289,"props":441,"children":442},{},[443],{"type":49,"value":444},"Parameter changes",{"type":49,"value":446},": set a flag in ",{"type":43,"tag":65,"props":448,"children":450},{"className":449},[],[451],{"type":49,"value":317},{"type":49,"value":453},", apply in ",{"type":43,"tag":65,"props":455,"children":457},{"className":456},[],[458],{"type":49,"value":299},{"type":49,"value":460},":",{"type":43,"tag":98,"props":462,"children":464},{"className":343,"code":463,"language":345,"meta":102,"style":102},"private bool _dataChanged;\n\nprotected override void OnParametersSet() => _dataChanged = true;\n\nprotected override async Task OnAfterRenderAsync(bool firstRender)\n{\n    if (firstRender) { \u002F* init *\u002F }\n    else if (_dataChanged && _chart is not null)\n    {\n        _dataChanged = false;\n        await _chart.UpdateDataAsync(DataPoints);\n    }\n}\n",[465],{"type":43,"tag":65,"props":466,"children":467},{"__ignoreMap":102},[468,476,483,491,498,505,512,520,528,535,543,552,560],{"type":43,"tag":108,"props":469,"children":470},{"class":110,"line":111},[471],{"type":43,"tag":108,"props":472,"children":473},{},[474],{"type":49,"value":475},"private bool _dataChanged;\n",{"type":43,"tag":108,"props":477,"children":478},{"class":110,"line":121},[479],{"type":43,"tag":108,"props":480,"children":481},{"emptyLinePlaceholder":363},[482],{"type":49,"value":366},{"type":43,"tag":108,"props":484,"children":485},{"class":110,"line":184},[486],{"type":43,"tag":108,"props":487,"children":488},{},[489],{"type":49,"value":490},"protected override void OnParametersSet() => _dataChanged = true;\n",{"type":43,"tag":108,"props":492,"children":493},{"class":110,"line":226},[494],{"type":43,"tag":108,"props":495,"children":496},{"emptyLinePlaceholder":363},[497],{"type":49,"value":366},{"type":43,"tag":108,"props":499,"children":500},{"class":110,"line":385},[501],{"type":43,"tag":108,"props":502,"children":503},{},[504],{"type":49,"value":374},{"type":43,"tag":108,"props":506,"children":507},{"class":110,"line":394},[508],{"type":43,"tag":108,"props":509,"children":510},{},[511],{"type":49,"value":382},{"type":43,"tag":108,"props":513,"children":514},{"class":110,"line":403},[515],{"type":43,"tag":108,"props":516,"children":517},{},[518],{"type":49,"value":519},"    if (firstRender) { \u002F* init *\u002F }\n",{"type":43,"tag":108,"props":521,"children":522},{"class":110,"line":412},[523],{"type":43,"tag":108,"props":524,"children":525},{},[526],{"type":49,"value":527},"    else if (_dataChanged && _chart is not null)\n",{"type":43,"tag":108,"props":529,"children":530},{"class":110,"line":421},[531],{"type":43,"tag":108,"props":532,"children":533},{},[534],{"type":49,"value":400},{"type":43,"tag":108,"props":536,"children":537},{"class":110,"line":430},[538],{"type":43,"tag":108,"props":539,"children":540},{},[541],{"type":49,"value":542},"        _dataChanged = false;\n",{"type":43,"tag":108,"props":544,"children":546},{"class":110,"line":545},11,[547],{"type":43,"tag":108,"props":548,"children":549},{},[550],{"type":49,"value":551},"        await _chart.UpdateDataAsync(DataPoints);\n",{"type":43,"tag":108,"props":553,"children":555},{"class":110,"line":554},12,[556],{"type":43,"tag":108,"props":557,"children":558},{},[559],{"type":49,"value":427},{"type":43,"tag":108,"props":561,"children":563},{"class":110,"line":562},13,[564],{"type":43,"tag":108,"props":565,"children":566},{},[567],{"type":49,"value":436},{"type":43,"tag":52,"props":569,"children":571},{"id":570},"_3-batch-related-operations",[572],{"type":49,"value":573},"3. Batch Related Operations",{"type":43,"tag":59,"props":575,"children":576},{},[577,579,584],{"type":49,"value":578},"Each JS interop call crosses the .NET-to-JS boundary (and in Blazor Server, the SignalR circuit). Batching applies in ",{"type":43,"tag":289,"props":580,"children":581},{},[582],{"type":49,"value":583},"both directions",{"type":49,"value":585}," — .NET→JS and JS→.NET.",{"type":43,"tag":587,"props":588,"children":590},"h3",{"id":589},"net-js-merge-consecutive-calls",[591],{"type":49,"value":592},".NET → JS: merge consecutive calls",{"type":43,"tag":59,"props":594,"children":595},{},[596],{"type":49,"value":597},"If the C# side makes two or more JS calls in a row, combine them into one JS function:",{"type":43,"tag":98,"props":599,"children":601},{"className":343,"code":600,"language":345,"meta":102,"style":102},"\u002F\u002F ❌ Two round-trips — theme and locale are always applied together\nawait _module.InvokeVoidAsync(\"applyTheme\", theme);\nawait _module.InvokeVoidAsync(\"applyLocale\", locale);\n\n\u002F\u002F ❌ Result of one call feeds into another — both can stay in JS\nvar token = await _module.InvokeAsync\u003Cstring>(\"createAccessToken\");\nawait _module.InvokeVoidAsync(\"storeToken\", token);\n",[602],{"type":43,"tag":65,"props":603,"children":604},{"__ignoreMap":102},[605,613,621,629,636,644,652],{"type":43,"tag":108,"props":606,"children":607},{"class":110,"line":111},[608],{"type":43,"tag":108,"props":609,"children":610},{},[611],{"type":49,"value":612},"\u002F\u002F ❌ Two round-trips — theme and locale are always applied together\n",{"type":43,"tag":108,"props":614,"children":615},{"class":110,"line":121},[616],{"type":43,"tag":108,"props":617,"children":618},{},[619],{"type":49,"value":620},"await _module.InvokeVoidAsync(\"applyTheme\", theme);\n",{"type":43,"tag":108,"props":622,"children":623},{"class":110,"line":184},[624],{"type":43,"tag":108,"props":625,"children":626},{},[627],{"type":49,"value":628},"await _module.InvokeVoidAsync(\"applyLocale\", locale);\n",{"type":43,"tag":108,"props":630,"children":631},{"class":110,"line":226},[632],{"type":43,"tag":108,"props":633,"children":634},{"emptyLinePlaceholder":363},[635],{"type":49,"value":366},{"type":43,"tag":108,"props":637,"children":638},{"class":110,"line":385},[639],{"type":43,"tag":108,"props":640,"children":641},{},[642],{"type":49,"value":643},"\u002F\u002F ❌ Result of one call feeds into another — both can stay in JS\n",{"type":43,"tag":108,"props":645,"children":646},{"class":110,"line":394},[647],{"type":43,"tag":108,"props":648,"children":649},{},[650],{"type":49,"value":651},"var token = await _module.InvokeAsync\u003Cstring>(\"createAccessToken\");\n",{"type":43,"tag":108,"props":653,"children":654},{"class":110,"line":403},[655],{"type":43,"tag":108,"props":656,"children":657},{},[658],{"type":49,"value":659},"await _module.InvokeVoidAsync(\"storeToken\", token);\n",{"type":43,"tag":98,"props":661,"children":663},{"className":100,"code":662,"language":18,"meta":102,"style":102},"\u002F\u002F ✅ One call applies both — no data dependency, no reason for two trips\nexport function applyPreferences(theme, locale) {\n    document.documentElement.dataset.theme = theme;\n    document.documentElement.lang = locale;\n}\n\n\u002F\u002F ✅ Chain stays in JS — the token never needs to cross the boundary\nexport function createAndStoreToken() {\n    const token = crypto.randomUUID();\n    sessionStorage.setItem('access-token', token);\n    return token;\n}\n",[664],{"type":43,"tag":65,"props":665,"children":666},{"__ignoreMap":102},[667,675,718,768,804,811,818,826,850,890,942,958],{"type":43,"tag":108,"props":668,"children":669},{"class":110,"line":111},[670],{"type":43,"tag":108,"props":671,"children":672},{"style":115},[673],{"type":49,"value":674},"\u002F\u002F ✅ One call applies both — no data dependency, no reason for two trips\n",{"type":43,"tag":108,"props":676,"children":677},{"class":110,"line":121},[678,682,686,691,695,700,704,709,713],{"type":43,"tag":108,"props":679,"children":680},{"style":125},[681],{"type":49,"value":78},{"type":43,"tag":108,"props":683,"children":684},{"style":130},[685],{"type":49,"value":133},{"type":43,"tag":108,"props":687,"children":688},{"style":136},[689],{"type":49,"value":690}," applyPreferences",{"type":43,"tag":108,"props":692,"children":693},{"style":142},[694],{"type":49,"value":145},{"type":43,"tag":108,"props":696,"children":697},{"style":148},[698],{"type":49,"value":699},"theme",{"type":43,"tag":108,"props":701,"children":702},{"style":142},[703],{"type":49,"value":156},{"type":43,"tag":108,"props":705,"children":706},{"style":148},[707],{"type":49,"value":708}," locale",{"type":43,"tag":108,"props":710,"children":711},{"style":142},[712],{"type":49,"value":166},{"type":43,"tag":108,"props":714,"children":715},{"style":142},[716],{"type":49,"value":717}," {\n",{"type":43,"tag":108,"props":719,"children":720},{"class":110,"line":184},[721,727,731,736,740,745,749,753,758,763],{"type":43,"tag":108,"props":722,"children":724},{"style":723},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[725],{"type":49,"value":726},"    document",{"type":43,"tag":108,"props":728,"children":729},{"style":142},[730],{"type":49,"value":278},{"type":43,"tag":108,"props":732,"children":733},{"style":723},[734],{"type":49,"value":735},"documentElement",{"type":43,"tag":108,"props":737,"children":738},{"style":142},[739],{"type":49,"value":278},{"type":43,"tag":108,"props":741,"children":742},{"style":723},[743],{"type":49,"value":744},"dataset",{"type":43,"tag":108,"props":746,"children":747},{"style":142},[748],{"type":49,"value":278},{"type":43,"tag":108,"props":750,"children":751},{"style":723},[752],{"type":49,"value":699},{"type":43,"tag":108,"props":754,"children":755},{"style":142},[756],{"type":49,"value":757}," =",{"type":43,"tag":108,"props":759,"children":760},{"style":723},[761],{"type":49,"value":762}," theme",{"type":43,"tag":108,"props":764,"children":765},{"style":142},[766],{"type":49,"value":767},";\n",{"type":43,"tag":108,"props":769,"children":770},{"class":110,"line":226},[771,775,779,783,787,792,796,800],{"type":43,"tag":108,"props":772,"children":773},{"style":723},[774],{"type":49,"value":726},{"type":43,"tag":108,"props":776,"children":777},{"style":142},[778],{"type":49,"value":278},{"type":43,"tag":108,"props":780,"children":781},{"style":723},[782],{"type":49,"value":735},{"type":43,"tag":108,"props":784,"children":785},{"style":142},[786],{"type":49,"value":278},{"type":43,"tag":108,"props":788,"children":789},{"style":723},[790],{"type":49,"value":791},"lang",{"type":43,"tag":108,"props":793,"children":794},{"style":142},[795],{"type":49,"value":757},{"type":43,"tag":108,"props":797,"children":798},{"style":723},[799],{"type":49,"value":708},{"type":43,"tag":108,"props":801,"children":802},{"style":142},[803],{"type":49,"value":767},{"type":43,"tag":108,"props":805,"children":806},{"class":110,"line":385},[807],{"type":43,"tag":108,"props":808,"children":809},{"style":142},[810],{"type":49,"value":436},{"type":43,"tag":108,"props":812,"children":813},{"class":110,"line":394},[814],{"type":43,"tag":108,"props":815,"children":816},{"emptyLinePlaceholder":363},[817],{"type":49,"value":366},{"type":43,"tag":108,"props":819,"children":820},{"class":110,"line":403},[821],{"type":43,"tag":108,"props":822,"children":823},{"style":115},[824],{"type":49,"value":825},"\u002F\u002F ✅ Chain stays in JS — the token never needs to cross the boundary\n",{"type":43,"tag":108,"props":827,"children":828},{"class":110,"line":412},[829,833,837,842,846],{"type":43,"tag":108,"props":830,"children":831},{"style":125},[832],{"type":49,"value":78},{"type":43,"tag":108,"props":834,"children":835},{"style":130},[836],{"type":49,"value":133},{"type":43,"tag":108,"props":838,"children":839},{"style":136},[840],{"type":49,"value":841}," createAndStoreToken",{"type":43,"tag":108,"props":843,"children":844},{"style":142},[845],{"type":49,"value":245},{"type":43,"tag":108,"props":847,"children":848},{"style":142},[849],{"type":49,"value":717},{"type":43,"tag":108,"props":851,"children":852},{"class":110,"line":421},[853,858,863,867,872,876,881,886],{"type":43,"tag":108,"props":854,"children":855},{"style":130},[856],{"type":49,"value":857},"    const",{"type":43,"tag":108,"props":859,"children":860},{"style":723},[861],{"type":49,"value":862}," token",{"type":43,"tag":108,"props":864,"children":865},{"style":142},[866],{"type":49,"value":757},{"type":43,"tag":108,"props":868,"children":869},{"style":723},[870],{"type":49,"value":871}," crypto",{"type":43,"tag":108,"props":873,"children":874},{"style":142},[875],{"type":49,"value":278},{"type":43,"tag":108,"props":877,"children":878},{"style":136},[879],{"type":49,"value":880},"randomUUID",{"type":43,"tag":108,"props":882,"children":884},{"style":883},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[885],{"type":49,"value":245},{"type":43,"tag":108,"props":887,"children":888},{"style":142},[889],{"type":49,"value":767},{"type":43,"tag":108,"props":891,"children":892},{"class":110,"line":430},[893,898,902,907,911,916,922,926,930,934,938],{"type":43,"tag":108,"props":894,"children":895},{"style":723},[896],{"type":49,"value":897},"    sessionStorage",{"type":43,"tag":108,"props":899,"children":900},{"style":142},[901],{"type":49,"value":278},{"type":43,"tag":108,"props":903,"children":904},{"style":136},[905],{"type":49,"value":906},"setItem",{"type":43,"tag":108,"props":908,"children":909},{"style":883},[910],{"type":49,"value":145},{"type":43,"tag":108,"props":912,"children":913},{"style":142},[914],{"type":49,"value":915},"'",{"type":43,"tag":108,"props":917,"children":919},{"style":918},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[920],{"type":49,"value":921},"access-token",{"type":43,"tag":108,"props":923,"children":924},{"style":142},[925],{"type":49,"value":915},{"type":43,"tag":108,"props":927,"children":928},{"style":142},[929],{"type":49,"value":156},{"type":43,"tag":108,"props":931,"children":932},{"style":723},[933],{"type":49,"value":862},{"type":43,"tag":108,"props":935,"children":936},{"style":883},[937],{"type":49,"value":166},{"type":43,"tag":108,"props":939,"children":940},{"style":142},[941],{"type":49,"value":767},{"type":43,"tag":108,"props":943,"children":944},{"class":110,"line":545},[945,950,954],{"type":43,"tag":108,"props":946,"children":947},{"style":125},[948],{"type":49,"value":949},"    return",{"type":43,"tag":108,"props":951,"children":952},{"style":723},[953],{"type":49,"value":862},{"type":43,"tag":108,"props":955,"children":956},{"style":142},[957],{"type":49,"value":767},{"type":43,"tag":108,"props":959,"children":960},{"class":110,"line":554},[961],{"type":43,"tag":108,"props":962,"children":963},{"style":142},[964],{"type":49,"value":436},{"type":43,"tag":587,"props":966,"children":968},{"id":967},"js-net-batch-callbacks",[969],{"type":49,"value":970},"JS → .NET: batch callbacks",{"type":43,"tag":59,"props":972,"children":973},{},[974,976,982],{"type":49,"value":975},"When JS needs to send multiple pieces of data back to .NET, send them in a single ",{"type":43,"tag":65,"props":977,"children":979},{"className":978},[],[980],{"type":49,"value":981},"invokeMethodAsync",{"type":49,"value":983}," call rather than making separate callbacks:",{"type":43,"tag":98,"props":985,"children":987},{"className":100,"code":986,"language":18,"meta":102,"style":102},"\u002F\u002F ❌ Two .NET round-trips from JS\nawait dotNetRef.invokeMethodAsync(ON_VOLUME_CHANGED, volume);\nawait dotNetRef.invokeMethodAsync(ON_PLAYBACK_CHANGED, isPlaying);\n\n\u002F\u002F ✅ One callback with all data\nawait dotNetRef.invokeMethodAsync(ON_PLAYER_STATE_CHANGED, { volume, isPlaying });\n",[988],{"type":43,"tag":65,"props":989,"children":990},{"__ignoreMap":102},[991,999,1037,1074,1081,1089],{"type":43,"tag":108,"props":992,"children":993},{"class":110,"line":111},[994],{"type":43,"tag":108,"props":995,"children":996},{"style":115},[997],{"type":49,"value":998},"\u002F\u002F ❌ Two .NET round-trips from JS\n",{"type":43,"tag":108,"props":1000,"children":1001},{"class":110,"line":121},[1002,1007,1011,1015,1019,1024,1028,1033],{"type":43,"tag":108,"props":1003,"children":1004},{"style":125},[1005],{"type":49,"value":1006},"await",{"type":43,"tag":108,"props":1008,"children":1009},{"style":723},[1010],{"type":49,"value":161},{"type":43,"tag":108,"props":1012,"children":1013},{"style":142},[1014],{"type":49,"value":278},{"type":43,"tag":108,"props":1016,"children":1017},{"style":136},[1018],{"type":49,"value":981},{"type":43,"tag":108,"props":1020,"children":1021},{"style":723},[1022],{"type":49,"value":1023},"(ON_VOLUME_CHANGED",{"type":43,"tag":108,"props":1025,"children":1026},{"style":142},[1027],{"type":49,"value":156},{"type":43,"tag":108,"props":1029,"children":1030},{"style":723},[1031],{"type":49,"value":1032}," volume)",{"type":43,"tag":108,"props":1034,"children":1035},{"style":142},[1036],{"type":49,"value":767},{"type":43,"tag":108,"props":1038,"children":1039},{"class":110,"line":184},[1040,1044,1048,1052,1056,1061,1065,1070],{"type":43,"tag":108,"props":1041,"children":1042},{"style":125},[1043],{"type":49,"value":1006},{"type":43,"tag":108,"props":1045,"children":1046},{"style":723},[1047],{"type":49,"value":161},{"type":43,"tag":108,"props":1049,"children":1050},{"style":142},[1051],{"type":49,"value":278},{"type":43,"tag":108,"props":1053,"children":1054},{"style":136},[1055],{"type":49,"value":981},{"type":43,"tag":108,"props":1057,"children":1058},{"style":723},[1059],{"type":49,"value":1060},"(ON_PLAYBACK_CHANGED",{"type":43,"tag":108,"props":1062,"children":1063},{"style":142},[1064],{"type":49,"value":156},{"type":43,"tag":108,"props":1066,"children":1067},{"style":723},[1068],{"type":49,"value":1069}," isPlaying)",{"type":43,"tag":108,"props":1071,"children":1072},{"style":142},[1073],{"type":49,"value":767},{"type":43,"tag":108,"props":1075,"children":1076},{"class":110,"line":226},[1077],{"type":43,"tag":108,"props":1078,"children":1079},{"emptyLinePlaceholder":363},[1080],{"type":49,"value":366},{"type":43,"tag":108,"props":1082,"children":1083},{"class":110,"line":385},[1084],{"type":43,"tag":108,"props":1085,"children":1086},{"style":115},[1087],{"type":49,"value":1088},"\u002F\u002F ✅ One callback with all data\n",{"type":43,"tag":108,"props":1090,"children":1091},{"class":110,"line":394},[1092,1096,1100,1104,1108,1113,1117,1121,1126,1130,1135,1140,1144],{"type":43,"tag":108,"props":1093,"children":1094},{"style":125},[1095],{"type":49,"value":1006},{"type":43,"tag":108,"props":1097,"children":1098},{"style":723},[1099],{"type":49,"value":161},{"type":43,"tag":108,"props":1101,"children":1102},{"style":142},[1103],{"type":49,"value":278},{"type":43,"tag":108,"props":1105,"children":1106},{"style":136},[1107],{"type":49,"value":981},{"type":43,"tag":108,"props":1109,"children":1110},{"style":723},[1111],{"type":49,"value":1112},"(ON_PLAYER_STATE_CHANGED",{"type":43,"tag":108,"props":1114,"children":1115},{"style":142},[1116],{"type":49,"value":156},{"type":43,"tag":108,"props":1118,"children":1119},{"style":142},[1120],{"type":49,"value":171},{"type":43,"tag":108,"props":1122,"children":1123},{"style":723},[1124],{"type":49,"value":1125}," volume",{"type":43,"tag":108,"props":1127,"children":1128},{"style":142},[1129],{"type":49,"value":156},{"type":43,"tag":108,"props":1131,"children":1132},{"style":723},[1133],{"type":49,"value":1134}," isPlaying ",{"type":43,"tag":108,"props":1136,"children":1137},{"style":142},[1138],{"type":49,"value":1139},"}",{"type":43,"tag":108,"props":1141,"children":1142},{"style":723},[1143],{"type":49,"value":166},{"type":43,"tag":108,"props":1145,"children":1146},{"style":142},[1147],{"type":49,"value":767},{"type":43,"tag":59,"props":1149,"children":1150},{},[1151,1156],{"type":43,"tag":289,"props":1152,"children":1153},{},[1154],{"type":49,"value":1155},"Rule",{"type":49,"value":1157},": if two interop calls always happen together from either side, merge them into one function.",{"type":43,"tag":52,"props":1159,"children":1161},{"id":1160},"_4-typed-interop-wrapper",[1162],{"type":49,"value":1163},"4. Typed Interop Wrapper",{"type":43,"tag":59,"props":1165,"children":1166},{},[1167],{"type":49,"value":1168},"Encapsulate interop for a feature in a plain class that owns the module lifecycle:",{"type":43,"tag":98,"props":1170,"children":1172},{"className":343,"code":1171,"language":345,"meta":102,"style":102},"public sealed class ChartInterop : IAsyncDisposable\n{\n    internal const string ModulePath = \".\u002FComponents\u002FChartPanel.razor.js\";\n    internal const string InitMethod = \"initialize\";\n    internal const string UpdateMethod = \"updateData\";\n    internal const string DisposeMethod = \"dispose\";\n\n    private readonly IJSRuntime _js;\n    private IJSObjectReference? _module;\n\n    public ChartInterop(IJSRuntime js) => _js = js;\n\n    private async ValueTask\u003CIJSObjectReference> GetModuleAsync()\n        => _module ??= await _js.InvokeAsync\u003CIJSObjectReference>(\"import\", ModulePath);\n\n    public async ValueTask InitializeAsync(ElementReference canvas)\n    {\n        var module = await GetModuleAsync();\n        await module.InvokeVoidAsync(InitMethod, canvas);\n    }\n\n    public async ValueTask UpdateDataAsync(IReadOnlyList\u003CDataPoint> points)\n    {\n        var module = await GetModuleAsync();\n        await module.InvokeVoidAsync(UpdateMethod, points);\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        try\n        {\n            if (_module is not null)\n            {\n                await _module.InvokeVoidAsync(DisposeMethod);\n                await _module.DisposeAsync();\n            }\n        }\n        catch (JSDisconnectedException) { }\n    }\n}\n",[1173],{"type":43,"tag":65,"props":1174,"children":1175},{"__ignoreMap":102},[1176,1184,1191,1199,1207,1215,1223,1230,1238,1246,1253,1261,1268,1276,1285,1293,1302,1310,1319,1328,1336,1344,1353,1361,1369,1378,1386,1394,1403,1411,1420,1429,1438,1447,1456,1465,1474,1483,1492,1500],{"type":43,"tag":108,"props":1177,"children":1178},{"class":110,"line":111},[1179],{"type":43,"tag":108,"props":1180,"children":1181},{},[1182],{"type":49,"value":1183},"public sealed class ChartInterop : IAsyncDisposable\n",{"type":43,"tag":108,"props":1185,"children":1186},{"class":110,"line":121},[1187],{"type":43,"tag":108,"props":1188,"children":1189},{},[1190],{"type":49,"value":382},{"type":43,"tag":108,"props":1192,"children":1193},{"class":110,"line":184},[1194],{"type":43,"tag":108,"props":1195,"children":1196},{},[1197],{"type":49,"value":1198},"    internal const string ModulePath = \".\u002FComponents\u002FChartPanel.razor.js\";\n",{"type":43,"tag":108,"props":1200,"children":1201},{"class":110,"line":226},[1202],{"type":43,"tag":108,"props":1203,"children":1204},{},[1205],{"type":49,"value":1206},"    internal const string InitMethod = \"initialize\";\n",{"type":43,"tag":108,"props":1208,"children":1209},{"class":110,"line":385},[1210],{"type":43,"tag":108,"props":1211,"children":1212},{},[1213],{"type":49,"value":1214},"    internal const string UpdateMethod = \"updateData\";\n",{"type":43,"tag":108,"props":1216,"children":1217},{"class":110,"line":394},[1218],{"type":43,"tag":108,"props":1219,"children":1220},{},[1221],{"type":49,"value":1222},"    internal const string DisposeMethod = \"dispose\";\n",{"type":43,"tag":108,"props":1224,"children":1225},{"class":110,"line":403},[1226],{"type":43,"tag":108,"props":1227,"children":1228},{"emptyLinePlaceholder":363},[1229],{"type":49,"value":366},{"type":43,"tag":108,"props":1231,"children":1232},{"class":110,"line":412},[1233],{"type":43,"tag":108,"props":1234,"children":1235},{},[1236],{"type":49,"value":1237},"    private readonly IJSRuntime _js;\n",{"type":43,"tag":108,"props":1239,"children":1240},{"class":110,"line":421},[1241],{"type":43,"tag":108,"props":1242,"children":1243},{},[1244],{"type":49,"value":1245},"    private IJSObjectReference? _module;\n",{"type":43,"tag":108,"props":1247,"children":1248},{"class":110,"line":430},[1249],{"type":43,"tag":108,"props":1250,"children":1251},{"emptyLinePlaceholder":363},[1252],{"type":49,"value":366},{"type":43,"tag":108,"props":1254,"children":1255},{"class":110,"line":545},[1256],{"type":43,"tag":108,"props":1257,"children":1258},{},[1259],{"type":49,"value":1260},"    public ChartInterop(IJSRuntime js) => _js = js;\n",{"type":43,"tag":108,"props":1262,"children":1263},{"class":110,"line":554},[1264],{"type":43,"tag":108,"props":1265,"children":1266},{"emptyLinePlaceholder":363},[1267],{"type":49,"value":366},{"type":43,"tag":108,"props":1269,"children":1270},{"class":110,"line":562},[1271],{"type":43,"tag":108,"props":1272,"children":1273},{},[1274],{"type":49,"value":1275},"    private async ValueTask\u003CIJSObjectReference> GetModuleAsync()\n",{"type":43,"tag":108,"props":1277,"children":1279},{"class":110,"line":1278},14,[1280],{"type":43,"tag":108,"props":1281,"children":1282},{},[1283],{"type":49,"value":1284},"        => _module ??= await _js.InvokeAsync\u003CIJSObjectReference>(\"import\", ModulePath);\n",{"type":43,"tag":108,"props":1286,"children":1288},{"class":110,"line":1287},15,[1289],{"type":43,"tag":108,"props":1290,"children":1291},{"emptyLinePlaceholder":363},[1292],{"type":49,"value":366},{"type":43,"tag":108,"props":1294,"children":1296},{"class":110,"line":1295},16,[1297],{"type":43,"tag":108,"props":1298,"children":1299},{},[1300],{"type":49,"value":1301},"    public async ValueTask InitializeAsync(ElementReference canvas)\n",{"type":43,"tag":108,"props":1303,"children":1305},{"class":110,"line":1304},17,[1306],{"type":43,"tag":108,"props":1307,"children":1308},{},[1309],{"type":49,"value":400},{"type":43,"tag":108,"props":1311,"children":1313},{"class":110,"line":1312},18,[1314],{"type":43,"tag":108,"props":1315,"children":1316},{},[1317],{"type":49,"value":1318},"        var module = await GetModuleAsync();\n",{"type":43,"tag":108,"props":1320,"children":1322},{"class":110,"line":1321},19,[1323],{"type":43,"tag":108,"props":1324,"children":1325},{},[1326],{"type":49,"value":1327},"        await module.InvokeVoidAsync(InitMethod, canvas);\n",{"type":43,"tag":108,"props":1329,"children":1331},{"class":110,"line":1330},20,[1332],{"type":43,"tag":108,"props":1333,"children":1334},{},[1335],{"type":49,"value":427},{"type":43,"tag":108,"props":1337,"children":1339},{"class":110,"line":1338},21,[1340],{"type":43,"tag":108,"props":1341,"children":1342},{"emptyLinePlaceholder":363},[1343],{"type":49,"value":366},{"type":43,"tag":108,"props":1345,"children":1347},{"class":110,"line":1346},22,[1348],{"type":43,"tag":108,"props":1349,"children":1350},{},[1351],{"type":49,"value":1352},"    public async ValueTask UpdateDataAsync(IReadOnlyList\u003CDataPoint> points)\n",{"type":43,"tag":108,"props":1354,"children":1356},{"class":110,"line":1355},23,[1357],{"type":43,"tag":108,"props":1358,"children":1359},{},[1360],{"type":49,"value":400},{"type":43,"tag":108,"props":1362,"children":1364},{"class":110,"line":1363},24,[1365],{"type":43,"tag":108,"props":1366,"children":1367},{},[1368],{"type":49,"value":1318},{"type":43,"tag":108,"props":1370,"children":1372},{"class":110,"line":1371},25,[1373],{"type":43,"tag":108,"props":1374,"children":1375},{},[1376],{"type":49,"value":1377},"        await module.InvokeVoidAsync(UpdateMethod, points);\n",{"type":43,"tag":108,"props":1379,"children":1381},{"class":110,"line":1380},26,[1382],{"type":43,"tag":108,"props":1383,"children":1384},{},[1385],{"type":49,"value":427},{"type":43,"tag":108,"props":1387,"children":1389},{"class":110,"line":1388},27,[1390],{"type":43,"tag":108,"props":1391,"children":1392},{"emptyLinePlaceholder":363},[1393],{"type":49,"value":366},{"type":43,"tag":108,"props":1395,"children":1397},{"class":110,"line":1396},28,[1398],{"type":43,"tag":108,"props":1399,"children":1400},{},[1401],{"type":49,"value":1402},"    public async ValueTask DisposeAsync()\n",{"type":43,"tag":108,"props":1404,"children":1406},{"class":110,"line":1405},29,[1407],{"type":43,"tag":108,"props":1408,"children":1409},{},[1410],{"type":49,"value":400},{"type":43,"tag":108,"props":1412,"children":1414},{"class":110,"line":1413},30,[1415],{"type":43,"tag":108,"props":1416,"children":1417},{},[1418],{"type":49,"value":1419},"        try\n",{"type":43,"tag":108,"props":1421,"children":1423},{"class":110,"line":1422},31,[1424],{"type":43,"tag":108,"props":1425,"children":1426},{},[1427],{"type":49,"value":1428},"        {\n",{"type":43,"tag":108,"props":1430,"children":1432},{"class":110,"line":1431},32,[1433],{"type":43,"tag":108,"props":1434,"children":1435},{},[1436],{"type":49,"value":1437},"            if (_module is not null)\n",{"type":43,"tag":108,"props":1439,"children":1441},{"class":110,"line":1440},33,[1442],{"type":43,"tag":108,"props":1443,"children":1444},{},[1445],{"type":49,"value":1446},"            {\n",{"type":43,"tag":108,"props":1448,"children":1450},{"class":110,"line":1449},34,[1451],{"type":43,"tag":108,"props":1452,"children":1453},{},[1454],{"type":49,"value":1455},"                await _module.InvokeVoidAsync(DisposeMethod);\n",{"type":43,"tag":108,"props":1457,"children":1459},{"class":110,"line":1458},35,[1460],{"type":43,"tag":108,"props":1461,"children":1462},{},[1463],{"type":49,"value":1464},"                await _module.DisposeAsync();\n",{"type":43,"tag":108,"props":1466,"children":1468},{"class":110,"line":1467},36,[1469],{"type":43,"tag":108,"props":1470,"children":1471},{},[1472],{"type":49,"value":1473},"            }\n",{"type":43,"tag":108,"props":1475,"children":1477},{"class":110,"line":1476},37,[1478],{"type":43,"tag":108,"props":1479,"children":1480},{},[1481],{"type":49,"value":1482},"        }\n",{"type":43,"tag":108,"props":1484,"children":1486},{"class":110,"line":1485},38,[1487],{"type":43,"tag":108,"props":1488,"children":1489},{},[1490],{"type":49,"value":1491},"        catch (JSDisconnectedException) { }\n",{"type":43,"tag":108,"props":1493,"children":1495},{"class":110,"line":1494},39,[1496],{"type":43,"tag":108,"props":1497,"children":1498},{},[1499],{"type":49,"value":427},{"type":43,"tag":108,"props":1501,"children":1503},{"class":110,"line":1502},40,[1504],{"type":43,"tag":108,"props":1505,"children":1506},{},[1507],{"type":49,"value":436},{"type":43,"tag":59,"props":1509,"children":1510},{},[1511],{"type":49,"value":1512},"The component creates and uses the wrapper with no magic strings:",{"type":43,"tag":98,"props":1514,"children":1518},{"className":1515,"code":1516,"language":1517,"meta":102,"style":102},"language-razor shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@inject IJSRuntime JS\n@implements IAsyncDisposable\n\n\u003Ccanvas @ref=\"_canvasRef\" width=\"600\" height=\"400\">\u003C\u002Fcanvas>\n\n@code {\n    private ElementReference _canvasRef;\n    private ChartInterop? _chart;\n\n    protected override async Task OnAfterRenderAsync(bool firstRender)\n    {\n        if (firstRender)\n        {\n            _chart = new ChartInterop(JS);\n            await _chart.InitializeAsync(_canvasRef);\n        }\n    }\n\n    async ValueTask IAsyncDisposable.DisposeAsync()\n    {\n        if (_chart is not null)\n            await _chart.DisposeAsync();\n    }\n}\n","razor",[1519],{"type":43,"tag":65,"props":1520,"children":1521},{"__ignoreMap":102},[1522,1530,1538,1545,1553,1560,1568,1576,1584,1591,1599,1606,1614,1621,1629,1637,1644,1651,1658,1666,1673,1681,1689,1696],{"type":43,"tag":108,"props":1523,"children":1524},{"class":110,"line":111},[1525],{"type":43,"tag":108,"props":1526,"children":1527},{},[1528],{"type":49,"value":1529},"@inject IJSRuntime JS\n",{"type":43,"tag":108,"props":1531,"children":1532},{"class":110,"line":121},[1533],{"type":43,"tag":108,"props":1534,"children":1535},{},[1536],{"type":49,"value":1537},"@implements IAsyncDisposable\n",{"type":43,"tag":108,"props":1539,"children":1540},{"class":110,"line":184},[1541],{"type":43,"tag":108,"props":1542,"children":1543},{"emptyLinePlaceholder":363},[1544],{"type":49,"value":366},{"type":43,"tag":108,"props":1546,"children":1547},{"class":110,"line":226},[1548],{"type":43,"tag":108,"props":1549,"children":1550},{},[1551],{"type":49,"value":1552},"\u003Ccanvas @ref=\"_canvasRef\" width=\"600\" height=\"400\">\u003C\u002Fcanvas>\n",{"type":43,"tag":108,"props":1554,"children":1555},{"class":110,"line":385},[1556],{"type":43,"tag":108,"props":1557,"children":1558},{"emptyLinePlaceholder":363},[1559],{"type":49,"value":366},{"type":43,"tag":108,"props":1561,"children":1562},{"class":110,"line":394},[1563],{"type":43,"tag":108,"props":1564,"children":1565},{},[1566],{"type":49,"value":1567},"@code {\n",{"type":43,"tag":108,"props":1569,"children":1570},{"class":110,"line":403},[1571],{"type":43,"tag":108,"props":1572,"children":1573},{},[1574],{"type":49,"value":1575},"    private ElementReference _canvasRef;\n",{"type":43,"tag":108,"props":1577,"children":1578},{"class":110,"line":412},[1579],{"type":43,"tag":108,"props":1580,"children":1581},{},[1582],{"type":49,"value":1583},"    private ChartInterop? _chart;\n",{"type":43,"tag":108,"props":1585,"children":1586},{"class":110,"line":421},[1587],{"type":43,"tag":108,"props":1588,"children":1589},{"emptyLinePlaceholder":363},[1590],{"type":49,"value":366},{"type":43,"tag":108,"props":1592,"children":1593},{"class":110,"line":430},[1594],{"type":43,"tag":108,"props":1595,"children":1596},{},[1597],{"type":49,"value":1598},"    protected override async Task OnAfterRenderAsync(bool firstRender)\n",{"type":43,"tag":108,"props":1600,"children":1601},{"class":110,"line":545},[1602],{"type":43,"tag":108,"props":1603,"children":1604},{},[1605],{"type":49,"value":400},{"type":43,"tag":108,"props":1607,"children":1608},{"class":110,"line":554},[1609],{"type":43,"tag":108,"props":1610,"children":1611},{},[1612],{"type":49,"value":1613},"        if (firstRender)\n",{"type":43,"tag":108,"props":1615,"children":1616},{"class":110,"line":562},[1617],{"type":43,"tag":108,"props":1618,"children":1619},{},[1620],{"type":49,"value":1428},{"type":43,"tag":108,"props":1622,"children":1623},{"class":110,"line":1278},[1624],{"type":43,"tag":108,"props":1625,"children":1626},{},[1627],{"type":49,"value":1628},"            _chart = new ChartInterop(JS);\n",{"type":43,"tag":108,"props":1630,"children":1631},{"class":110,"line":1287},[1632],{"type":43,"tag":108,"props":1633,"children":1634},{},[1635],{"type":49,"value":1636},"            await _chart.InitializeAsync(_canvasRef);\n",{"type":43,"tag":108,"props":1638,"children":1639},{"class":110,"line":1295},[1640],{"type":43,"tag":108,"props":1641,"children":1642},{},[1643],{"type":49,"value":1482},{"type":43,"tag":108,"props":1645,"children":1646},{"class":110,"line":1304},[1647],{"type":43,"tag":108,"props":1648,"children":1649},{},[1650],{"type":49,"value":427},{"type":43,"tag":108,"props":1652,"children":1653},{"class":110,"line":1312},[1654],{"type":43,"tag":108,"props":1655,"children":1656},{"emptyLinePlaceholder":363},[1657],{"type":49,"value":366},{"type":43,"tag":108,"props":1659,"children":1660},{"class":110,"line":1321},[1661],{"type":43,"tag":108,"props":1662,"children":1663},{},[1664],{"type":49,"value":1665},"    async ValueTask IAsyncDisposable.DisposeAsync()\n",{"type":43,"tag":108,"props":1667,"children":1668},{"class":110,"line":1330},[1669],{"type":43,"tag":108,"props":1670,"children":1671},{},[1672],{"type":49,"value":400},{"type":43,"tag":108,"props":1674,"children":1675},{"class":110,"line":1338},[1676],{"type":43,"tag":108,"props":1677,"children":1678},{},[1679],{"type":49,"value":1680},"        if (_chart is not null)\n",{"type":43,"tag":108,"props":1682,"children":1683},{"class":110,"line":1346},[1684],{"type":43,"tag":108,"props":1685,"children":1686},{},[1687],{"type":49,"value":1688},"            await _chart.DisposeAsync();\n",{"type":43,"tag":108,"props":1690,"children":1691},{"class":110,"line":1355},[1692],{"type":43,"tag":108,"props":1693,"children":1694},{},[1695],{"type":49,"value":427},{"type":43,"tag":108,"props":1697,"children":1698},{"class":110,"line":1363},[1699],{"type":43,"tag":108,"props":1700,"children":1701},{},[1702],{"type":49,"value":436},{"type":43,"tag":59,"props":1704,"children":1705},{},[1706,1708,1714],{"type":49,"value":1707},"Prefer a concrete class over interface + implementation for interop wrappers. For unit testing, substitute ",{"type":43,"tag":65,"props":1709,"children":1711},{"className":1710},[],[1712],{"type":49,"value":1713},"IJSRuntime",{"type":49,"value":1715}," directly (it is already an interface).",{"type":43,"tag":52,"props":1717,"children":1719},{"id":1718},"_5-dotnetobjectreference-for-js-to-net-callbacks",[1720],{"type":49,"value":1721},"5. DotNetObjectReference for JS-to-.NET Callbacks",{"type":43,"tag":98,"props":1723,"children":1725},{"className":343,"code":1724,"language":345,"meta":102,"style":102},"_dotNetRef = DotNetObjectReference.Create(this);\nawait _module.InvokeVoidAsync(\"initialize\", _dotNetRef);\n",[1726],{"type":43,"tag":65,"props":1727,"children":1728},{"__ignoreMap":102},[1729,1737],{"type":43,"tag":108,"props":1730,"children":1731},{"class":110,"line":111},[1732],{"type":43,"tag":108,"props":1733,"children":1734},{},[1735],{"type":49,"value":1736},"_dotNetRef = DotNetObjectReference.Create(this);\n",{"type":43,"tag":108,"props":1738,"children":1739},{"class":110,"line":121},[1740],{"type":43,"tag":108,"props":1741,"children":1742},{},[1743],{"type":49,"value":1744},"await _module.InvokeVoidAsync(\"initialize\", _dotNetRef);\n",{"type":43,"tag":59,"props":1746,"children":1747},{},[1748,1750,1756,1758,1764,1765,1770,1772,1778,1780,1786],{"type":49,"value":1749},"On the JS side, wrap the ",{"type":43,"tag":65,"props":1751,"children":1753},{"className":1752},[],[1754],{"type":49,"value":1755},"dotNetRef",{"type":49,"value":1757}," in a class. Use ",{"type":43,"tag":65,"props":1759,"children":1761},{"className":1760},[],[1762],{"type":49,"value":1763},"async",{"type":49,"value":332},{"type":43,"tag":65,"props":1766,"children":1768},{"className":1767},[],[1769],{"type":49,"value":1006},{"type":49,"value":1771}," with ",{"type":43,"tag":65,"props":1773,"children":1775},{"className":1774},[],[1776],{"type":49,"value":1777},"try\u002Fcatch",{"type":49,"value":1779}," (not ",{"type":43,"tag":65,"props":1781,"children":1783},{"className":1782},[],[1784],{"type":49,"value":1785},".catch()",{"type":49,"value":1787},") to guard against circuit loss. Define .NET method name constants at the top:",{"type":43,"tag":98,"props":1789,"children":1791},{"className":100,"code":1790,"language":18,"meta":102,"style":102},"const ON_CLIPBOARD_CHANGED = 'OnClipboardChanged';\n\nclass ClipboardMonitor {\n    #dotNetRef;\n    #abortController;\n\n    constructor(dotNetRef) {\n        this.#dotNetRef = dotNetRef;\n        this.#abortController = new AbortController();\n    }\n\n    start() {\n        document.addEventListener('copy', async () => {\n            try {\n                const text = await navigator.clipboard.readText();\n                await this.#dotNetRef.invokeMethodAsync(ON_CLIPBOARD_CHANGED, text);\n            } catch { \u002F* circuit disconnected or clipboard denied *\u002F }\n        }, { signal: this.#abortController.signal });\n    }\n\n    dispose() {\n        this.#abortController.abort();\n    }\n}\n\nlet monitor;\nexport function initialize(dotNetRef) {\n    monitor = new ClipboardMonitor(dotNetRef);\n    monitor.start();\n}\n\nexport function dispose() {\n    monitor?.dispose();\n}\n",[1792],{"type":43,"tag":65,"props":1793,"children":1794},{"__ignoreMap":102},[1795,1831,1838,1856,1868,1880,1887,1911,1936,1970,1977,1984,2000,2057,2069,2122,2172,2198,2249,2256,2263,2279,2307,2314,2321,2328,2345,2376,2412,2436,2443,2450,2473,2498],{"type":43,"tag":108,"props":1796,"children":1797},{"class":110,"line":111},[1798,1803,1808,1813,1818,1823,1827],{"type":43,"tag":108,"props":1799,"children":1800},{"style":130},[1801],{"type":49,"value":1802},"const",{"type":43,"tag":108,"props":1804,"children":1805},{"style":723},[1806],{"type":49,"value":1807}," ON_CLIPBOARD_CHANGED ",{"type":43,"tag":108,"props":1809,"children":1810},{"style":142},[1811],{"type":49,"value":1812},"=",{"type":43,"tag":108,"props":1814,"children":1815},{"style":142},[1816],{"type":49,"value":1817}," '",{"type":43,"tag":108,"props":1819,"children":1820},{"style":918},[1821],{"type":49,"value":1822},"OnClipboardChanged",{"type":43,"tag":108,"props":1824,"children":1825},{"style":142},[1826],{"type":49,"value":915},{"type":43,"tag":108,"props":1828,"children":1829},{"style":142},[1830],{"type":49,"value":767},{"type":43,"tag":108,"props":1832,"children":1833},{"class":110,"line":121},[1834],{"type":43,"tag":108,"props":1835,"children":1836},{"emptyLinePlaceholder":363},[1837],{"type":49,"value":366},{"type":43,"tag":108,"props":1839,"children":1840},{"class":110,"line":184},[1841,1846,1852],{"type":43,"tag":108,"props":1842,"children":1843},{"style":130},[1844],{"type":49,"value":1845},"class",{"type":43,"tag":108,"props":1847,"children":1849},{"style":1848},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1850],{"type":49,"value":1851}," ClipboardMonitor",{"type":43,"tag":108,"props":1853,"children":1854},{"style":142},[1855],{"type":49,"value":717},{"type":43,"tag":108,"props":1857,"children":1858},{"class":110,"line":226},[1859,1864],{"type":43,"tag":108,"props":1860,"children":1861},{"style":883},[1862],{"type":49,"value":1863},"    #dotNetRef",{"type":43,"tag":108,"props":1865,"children":1866},{"style":142},[1867],{"type":49,"value":767},{"type":43,"tag":108,"props":1869,"children":1870},{"class":110,"line":385},[1871,1876],{"type":43,"tag":108,"props":1872,"children":1873},{"style":883},[1874],{"type":49,"value":1875},"    #abortController",{"type":43,"tag":108,"props":1877,"children":1878},{"style":142},[1879],{"type":49,"value":767},{"type":43,"tag":108,"props":1881,"children":1882},{"class":110,"line":394},[1883],{"type":43,"tag":108,"props":1884,"children":1885},{"emptyLinePlaceholder":363},[1886],{"type":49,"value":366},{"type":43,"tag":108,"props":1888,"children":1889},{"class":110,"line":403},[1890,1895,1899,1903,1907],{"type":43,"tag":108,"props":1891,"children":1892},{"style":130},[1893],{"type":49,"value":1894},"    constructor",{"type":43,"tag":108,"props":1896,"children":1897},{"style":142},[1898],{"type":49,"value":145},{"type":43,"tag":108,"props":1900,"children":1901},{"style":148},[1902],{"type":49,"value":1755},{"type":43,"tag":108,"props":1904,"children":1905},{"style":142},[1906],{"type":49,"value":166},{"type":43,"tag":108,"props":1908,"children":1909},{"style":142},[1910],{"type":49,"value":717},{"type":43,"tag":108,"props":1912,"children":1913},{"class":110,"line":412},[1914,1919,1924,1928,1932],{"type":43,"tag":108,"props":1915,"children":1916},{"style":142},[1917],{"type":49,"value":1918},"        this.",{"type":43,"tag":108,"props":1920,"children":1921},{"style":723},[1922],{"type":49,"value":1923},"#dotNetRef",{"type":43,"tag":108,"props":1925,"children":1926},{"style":142},[1927],{"type":49,"value":757},{"type":43,"tag":108,"props":1929,"children":1930},{"style":723},[1931],{"type":49,"value":161},{"type":43,"tag":108,"props":1933,"children":1934},{"style":142},[1935],{"type":49,"value":767},{"type":43,"tag":108,"props":1937,"children":1938},{"class":110,"line":421},[1939,1943,1948,1952,1957,1962,1966],{"type":43,"tag":108,"props":1940,"children":1941},{"style":142},[1942],{"type":49,"value":1918},{"type":43,"tag":108,"props":1944,"children":1945},{"style":723},[1946],{"type":49,"value":1947},"#abortController",{"type":43,"tag":108,"props":1949,"children":1950},{"style":142},[1951],{"type":49,"value":757},{"type":43,"tag":108,"props":1953,"children":1954},{"style":142},[1955],{"type":49,"value":1956}," new",{"type":43,"tag":108,"props":1958,"children":1959},{"style":136},[1960],{"type":49,"value":1961}," AbortController",{"type":43,"tag":108,"props":1963,"children":1964},{"style":883},[1965],{"type":49,"value":245},{"type":43,"tag":108,"props":1967,"children":1968},{"style":142},[1969],{"type":49,"value":767},{"type":43,"tag":108,"props":1971,"children":1972},{"class":110,"line":430},[1973],{"type":43,"tag":108,"props":1974,"children":1975},{"style":142},[1976],{"type":49,"value":427},{"type":43,"tag":108,"props":1978,"children":1979},{"class":110,"line":545},[1980],{"type":43,"tag":108,"props":1981,"children":1982},{"emptyLinePlaceholder":363},[1983],{"type":49,"value":366},{"type":43,"tag":108,"props":1985,"children":1986},{"class":110,"line":554},[1987,1992,1996],{"type":43,"tag":108,"props":1988,"children":1989},{"style":883},[1990],{"type":49,"value":1991},"    start",{"type":43,"tag":108,"props":1993,"children":1994},{"style":142},[1995],{"type":49,"value":245},{"type":43,"tag":108,"props":1997,"children":1998},{"style":142},[1999],{"type":49,"value":717},{"type":43,"tag":108,"props":2001,"children":2002},{"class":110,"line":562},[2003,2008,2012,2017,2021,2025,2030,2034,2038,2043,2048,2053],{"type":43,"tag":108,"props":2004,"children":2005},{"style":723},[2006],{"type":49,"value":2007},"        document",{"type":43,"tag":108,"props":2009,"children":2010},{"style":142},[2011],{"type":49,"value":278},{"type":43,"tag":108,"props":2013,"children":2014},{"style":136},[2015],{"type":49,"value":2016},"addEventListener",{"type":43,"tag":108,"props":2018,"children":2019},{"style":883},[2020],{"type":49,"value":145},{"type":43,"tag":108,"props":2022,"children":2023},{"style":142},[2024],{"type":49,"value":915},{"type":43,"tag":108,"props":2026,"children":2027},{"style":918},[2028],{"type":49,"value":2029},"copy",{"type":43,"tag":108,"props":2031,"children":2032},{"style":142},[2033],{"type":49,"value":915},{"type":43,"tag":108,"props":2035,"children":2036},{"style":142},[2037],{"type":49,"value":156},{"type":43,"tag":108,"props":2039,"children":2040},{"style":130},[2041],{"type":49,"value":2042}," async",{"type":43,"tag":108,"props":2044,"children":2045},{"style":142},[2046],{"type":49,"value":2047}," ()",{"type":43,"tag":108,"props":2049,"children":2050},{"style":130},[2051],{"type":49,"value":2052}," =>",{"type":43,"tag":108,"props":2054,"children":2055},{"style":142},[2056],{"type":49,"value":717},{"type":43,"tag":108,"props":2058,"children":2059},{"class":110,"line":1278},[2060,2065],{"type":43,"tag":108,"props":2061,"children":2062},{"style":125},[2063],{"type":49,"value":2064},"            try",{"type":43,"tag":108,"props":2066,"children":2067},{"style":142},[2068],{"type":49,"value":717},{"type":43,"tag":108,"props":2070,"children":2071},{"class":110,"line":1287},[2072,2077,2082,2086,2091,2096,2100,2105,2109,2114,2118],{"type":43,"tag":108,"props":2073,"children":2074},{"style":130},[2075],{"type":49,"value":2076},"                const",{"type":43,"tag":108,"props":2078,"children":2079},{"style":723},[2080],{"type":49,"value":2081}," text",{"type":43,"tag":108,"props":2083,"children":2084},{"style":142},[2085],{"type":49,"value":757},{"type":43,"tag":108,"props":2087,"children":2088},{"style":125},[2089],{"type":49,"value":2090}," await",{"type":43,"tag":108,"props":2092,"children":2093},{"style":723},[2094],{"type":49,"value":2095}," navigator",{"type":43,"tag":108,"props":2097,"children":2098},{"style":142},[2099],{"type":49,"value":278},{"type":43,"tag":108,"props":2101,"children":2102},{"style":723},[2103],{"type":49,"value":2104},"clipboard",{"type":43,"tag":108,"props":2106,"children":2107},{"style":142},[2108],{"type":49,"value":278},{"type":43,"tag":108,"props":2110,"children":2111},{"style":136},[2112],{"type":49,"value":2113},"readText",{"type":43,"tag":108,"props":2115,"children":2116},{"style":883},[2117],{"type":49,"value":245},{"type":43,"tag":108,"props":2119,"children":2120},{"style":142},[2121],{"type":49,"value":767},{"type":43,"tag":108,"props":2123,"children":2124},{"class":110,"line":1295},[2125,2130,2135,2139,2143,2147,2151,2156,2160,2164,2168],{"type":43,"tag":108,"props":2126,"children":2127},{"style":125},[2128],{"type":49,"value":2129},"                await",{"type":43,"tag":108,"props":2131,"children":2132},{"style":142},[2133],{"type":49,"value":2134}," this.",{"type":43,"tag":108,"props":2136,"children":2137},{"style":723},[2138],{"type":49,"value":1923},{"type":43,"tag":108,"props":2140,"children":2141},{"style":142},[2142],{"type":49,"value":278},{"type":43,"tag":108,"props":2144,"children":2145},{"style":136},[2146],{"type":49,"value":981},{"type":43,"tag":108,"props":2148,"children":2149},{"style":883},[2150],{"type":49,"value":145},{"type":43,"tag":108,"props":2152,"children":2153},{"style":723},[2154],{"type":49,"value":2155},"ON_CLIPBOARD_CHANGED",{"type":43,"tag":108,"props":2157,"children":2158},{"style":142},[2159],{"type":49,"value":156},{"type":43,"tag":108,"props":2161,"children":2162},{"style":723},[2163],{"type":49,"value":2081},{"type":43,"tag":108,"props":2165,"children":2166},{"style":883},[2167],{"type":49,"value":166},{"type":43,"tag":108,"props":2169,"children":2170},{"style":142},[2171],{"type":49,"value":767},{"type":43,"tag":108,"props":2173,"children":2174},{"class":110,"line":1304},[2175,2180,2185,2189,2194],{"type":43,"tag":108,"props":2176,"children":2177},{"style":142},[2178],{"type":49,"value":2179},"            }",{"type":43,"tag":108,"props":2181,"children":2182},{"style":125},[2183],{"type":49,"value":2184}," catch",{"type":43,"tag":108,"props":2186,"children":2187},{"style":142},[2188],{"type":49,"value":171},{"type":43,"tag":108,"props":2190,"children":2191},{"style":115},[2192],{"type":49,"value":2193}," \u002F* circuit disconnected or clipboard denied *\u002F",{"type":43,"tag":108,"props":2195,"children":2196},{"style":142},[2197],{"type":49,"value":181},{"type":43,"tag":108,"props":2199,"children":2200},{"class":110,"line":1312},[2201,2206,2210,2215,2219,2223,2227,2231,2236,2241,2245],{"type":43,"tag":108,"props":2202,"children":2203},{"style":142},[2204],{"type":49,"value":2205},"        },",{"type":43,"tag":108,"props":2207,"children":2208},{"style":142},[2209],{"type":49,"value":171},{"type":43,"tag":108,"props":2211,"children":2212},{"style":883},[2213],{"type":49,"value":2214}," signal",{"type":43,"tag":108,"props":2216,"children":2217},{"style":142},[2218],{"type":49,"value":460},{"type":43,"tag":108,"props":2220,"children":2221},{"style":142},[2222],{"type":49,"value":2134},{"type":43,"tag":108,"props":2224,"children":2225},{"style":723},[2226],{"type":49,"value":1947},{"type":43,"tag":108,"props":2228,"children":2229},{"style":142},[2230],{"type":49,"value":278},{"type":43,"tag":108,"props":2232,"children":2233},{"style":723},[2234],{"type":49,"value":2235},"signal",{"type":43,"tag":108,"props":2237,"children":2238},{"style":142},[2239],{"type":49,"value":2240}," }",{"type":43,"tag":108,"props":2242,"children":2243},{"style":883},[2244],{"type":49,"value":166},{"type":43,"tag":108,"props":2246,"children":2247},{"style":142},[2248],{"type":49,"value":767},{"type":43,"tag":108,"props":2250,"children":2251},{"class":110,"line":1321},[2252],{"type":43,"tag":108,"props":2253,"children":2254},{"style":142},[2255],{"type":49,"value":427},{"type":43,"tag":108,"props":2257,"children":2258},{"class":110,"line":1330},[2259],{"type":43,"tag":108,"props":2260,"children":2261},{"emptyLinePlaceholder":363},[2262],{"type":49,"value":366},{"type":43,"tag":108,"props":2264,"children":2265},{"class":110,"line":1338},[2266,2271,2275],{"type":43,"tag":108,"props":2267,"children":2268},{"style":883},[2269],{"type":49,"value":2270},"    dispose",{"type":43,"tag":108,"props":2272,"children":2273},{"style":142},[2274],{"type":49,"value":245},{"type":43,"tag":108,"props":2276,"children":2277},{"style":142},[2278],{"type":49,"value":717},{"type":43,"tag":108,"props":2280,"children":2281},{"class":110,"line":1346},[2282,2286,2290,2294,2299,2303],{"type":43,"tag":108,"props":2283,"children":2284},{"style":142},[2285],{"type":49,"value":1918},{"type":43,"tag":108,"props":2287,"children":2288},{"style":723},[2289],{"type":49,"value":1947},{"type":43,"tag":108,"props":2291,"children":2292},{"style":142},[2293],{"type":49,"value":278},{"type":43,"tag":108,"props":2295,"children":2296},{"style":136},[2297],{"type":49,"value":2298},"abort",{"type":43,"tag":108,"props":2300,"children":2301},{"style":883},[2302],{"type":49,"value":245},{"type":43,"tag":108,"props":2304,"children":2305},{"style":142},[2306],{"type":49,"value":767},{"type":43,"tag":108,"props":2308,"children":2309},{"class":110,"line":1355},[2310],{"type":43,"tag":108,"props":2311,"children":2312},{"style":142},[2313],{"type":49,"value":427},{"type":43,"tag":108,"props":2315,"children":2316},{"class":110,"line":1363},[2317],{"type":43,"tag":108,"props":2318,"children":2319},{"style":142},[2320],{"type":49,"value":436},{"type":43,"tag":108,"props":2322,"children":2323},{"class":110,"line":1371},[2324],{"type":43,"tag":108,"props":2325,"children":2326},{"emptyLinePlaceholder":363},[2327],{"type":49,"value":366},{"type":43,"tag":108,"props":2329,"children":2330},{"class":110,"line":1380},[2331,2336,2341],{"type":43,"tag":108,"props":2332,"children":2333},{"style":130},[2334],{"type":49,"value":2335},"let",{"type":43,"tag":108,"props":2337,"children":2338},{"style":723},[2339],{"type":49,"value":2340}," monitor",{"type":43,"tag":108,"props":2342,"children":2343},{"style":142},[2344],{"type":49,"value":767},{"type":43,"tag":108,"props":2346,"children":2347},{"class":110,"line":1388},[2348,2352,2356,2360,2364,2368,2372],{"type":43,"tag":108,"props":2349,"children":2350},{"style":125},[2351],{"type":49,"value":78},{"type":43,"tag":108,"props":2353,"children":2354},{"style":130},[2355],{"type":49,"value":133},{"type":43,"tag":108,"props":2357,"children":2358},{"style":136},[2359],{"type":49,"value":139},{"type":43,"tag":108,"props":2361,"children":2362},{"style":142},[2363],{"type":49,"value":145},{"type":43,"tag":108,"props":2365,"children":2366},{"style":148},[2367],{"type":49,"value":1755},{"type":43,"tag":108,"props":2369,"children":2370},{"style":142},[2371],{"type":49,"value":166},{"type":43,"tag":108,"props":2373,"children":2374},{"style":142},[2375],{"type":49,"value":717},{"type":43,"tag":108,"props":2377,"children":2378},{"class":110,"line":1396},[2379,2384,2388,2392,2396,2400,2404,2408],{"type":43,"tag":108,"props":2380,"children":2381},{"style":723},[2382],{"type":49,"value":2383},"    monitor",{"type":43,"tag":108,"props":2385,"children":2386},{"style":142},[2387],{"type":49,"value":757},{"type":43,"tag":108,"props":2389,"children":2390},{"style":142},[2391],{"type":49,"value":1956},{"type":43,"tag":108,"props":2393,"children":2394},{"style":136},[2395],{"type":49,"value":1851},{"type":43,"tag":108,"props":2397,"children":2398},{"style":883},[2399],{"type":49,"value":145},{"type":43,"tag":108,"props":2401,"children":2402},{"style":723},[2403],{"type":49,"value":1755},{"type":43,"tag":108,"props":2405,"children":2406},{"style":883},[2407],{"type":49,"value":166},{"type":43,"tag":108,"props":2409,"children":2410},{"style":142},[2411],{"type":49,"value":767},{"type":43,"tag":108,"props":2413,"children":2414},{"class":110,"line":1405},[2415,2419,2423,2428,2432],{"type":43,"tag":108,"props":2416,"children":2417},{"style":723},[2418],{"type":49,"value":2383},{"type":43,"tag":108,"props":2420,"children":2421},{"style":142},[2422],{"type":49,"value":278},{"type":43,"tag":108,"props":2424,"children":2425},{"style":136},[2426],{"type":49,"value":2427},"start",{"type":43,"tag":108,"props":2429,"children":2430},{"style":883},[2431],{"type":49,"value":245},{"type":43,"tag":108,"props":2433,"children":2434},{"style":142},[2435],{"type":49,"value":767},{"type":43,"tag":108,"props":2437,"children":2438},{"class":110,"line":1413},[2439],{"type":43,"tag":108,"props":2440,"children":2441},{"style":142},[2442],{"type":49,"value":436},{"type":43,"tag":108,"props":2444,"children":2445},{"class":110,"line":1422},[2446],{"type":43,"tag":108,"props":2447,"children":2448},{"emptyLinePlaceholder":363},[2449],{"type":49,"value":366},{"type":43,"tag":108,"props":2451,"children":2452},{"class":110,"line":1431},[2453,2457,2461,2465,2469],{"type":43,"tag":108,"props":2454,"children":2455},{"style":125},[2456],{"type":49,"value":78},{"type":43,"tag":108,"props":2458,"children":2459},{"style":130},[2460],{"type":49,"value":133},{"type":43,"tag":108,"props":2462,"children":2463},{"style":136},[2464],{"type":49,"value":240},{"type":43,"tag":108,"props":2466,"children":2467},{"style":142},[2468],{"type":49,"value":245},{"type":43,"tag":108,"props":2470,"children":2471},{"style":142},[2472],{"type":49,"value":717},{"type":43,"tag":108,"props":2474,"children":2475},{"class":110,"line":1440},[2476,2480,2485,2490,2494],{"type":43,"tag":108,"props":2477,"children":2478},{"style":723},[2479],{"type":49,"value":2383},{"type":43,"tag":108,"props":2481,"children":2482},{"style":142},[2483],{"type":49,"value":2484},"?.",{"type":43,"tag":108,"props":2486,"children":2487},{"style":136},[2488],{"type":49,"value":2489},"dispose",{"type":43,"tag":108,"props":2491,"children":2492},{"style":883},[2493],{"type":49,"value":245},{"type":43,"tag":108,"props":2495,"children":2496},{"style":142},[2497],{"type":49,"value":767},{"type":43,"tag":108,"props":2499,"children":2500},{"class":110,"line":1449},[2501],{"type":43,"tag":108,"props":2502,"children":2503},{"style":142},[2504],{"type":49,"value":436},{"type":43,"tag":59,"props":2506,"children":2507},{},[2508],{"type":49,"value":2509},"Rules:",{"type":43,"tag":2511,"props":2512,"children":2513},"ul",{},[2514,2539,2611,2630,2642],{"type":43,"tag":2515,"props":2516,"children":2517},"li",{},[2518,2524,2526,2537],{"type":43,"tag":65,"props":2519,"children":2521},{"className":2520},[],[2522],{"type":49,"value":2523},"[JSInvokable]",{"type":49,"value":2525}," methods ",{"type":43,"tag":289,"props":2527,"children":2528},{},[2529,2531],{"type":49,"value":2530},"must be ",{"type":43,"tag":65,"props":2532,"children":2534},{"className":2533},[],[2535],{"type":49,"value":2536},"public",{"type":49,"value":2538}," — private\u002Finternal silently fails at runtime",{"type":43,"tag":2515,"props":2540,"children":2541},{},[2542,2544,2550,2552,2557,2559,2564,2566],{"type":49,"value":2543},"Wrap ",{"type":43,"tag":65,"props":2545,"children":2547},{"className":2546},[],[2548],{"type":49,"value":2549},"StateHasChanged",{"type":49,"value":2551}," in ",{"type":43,"tag":65,"props":2553,"children":2555},{"className":2554},[],[2556],{"type":49,"value":330},{"type":49,"value":2558}," inside ",{"type":43,"tag":65,"props":2560,"children":2562},{"className":2561},[],[2563],{"type":49,"value":2523},{"type":49,"value":2565}," callbacks:\n",{"type":43,"tag":98,"props":2567,"children":2569},{"className":343,"code":2568,"language":345,"meta":102,"style":102},"[JSInvokable]\npublic async Task OnClipboardChanged(string text)\n{\n    await InvokeAsync(() => { _lastClipboard = text; StateHasChanged(); });\n}\n",[2570],{"type":43,"tag":65,"props":2571,"children":2572},{"__ignoreMap":102},[2573,2581,2589,2596,2604],{"type":43,"tag":108,"props":2574,"children":2575},{"class":110,"line":111},[2576],{"type":43,"tag":108,"props":2577,"children":2578},{},[2579],{"type":49,"value":2580},"[JSInvokable]\n",{"type":43,"tag":108,"props":2582,"children":2583},{"class":110,"line":121},[2584],{"type":43,"tag":108,"props":2585,"children":2586},{},[2587],{"type":49,"value":2588},"public async Task OnClipboardChanged(string text)\n",{"type":43,"tag":108,"props":2590,"children":2591},{"class":110,"line":184},[2592],{"type":43,"tag":108,"props":2593,"children":2594},{},[2595],{"type":49,"value":382},{"type":43,"tag":108,"props":2597,"children":2598},{"class":110,"line":226},[2599],{"type":43,"tag":108,"props":2600,"children":2601},{},[2602],{"type":49,"value":2603},"    await InvokeAsync(() => { _lastClipboard = text; StateHasChanged(); });\n",{"type":43,"tag":108,"props":2605,"children":2606},{"class":110,"line":385},[2607],{"type":43,"tag":108,"props":2608,"children":2609},{},[2610],{"type":49,"value":436},{"type":43,"tag":2515,"props":2612,"children":2613},{},[2614,2616,2621,2623,2628],{"type":49,"value":2615},"Always ",{"type":43,"tag":65,"props":2617,"children":2619},{"className":2618},[],[2620],{"type":49,"value":1777},{"type":49,"value":2622}," around ",{"type":43,"tag":65,"props":2624,"children":2626},{"className":2625},[],[2627],{"type":49,"value":981},{"type":49,"value":2629}," in JS — circuit loss throws",{"type":43,"tag":2515,"props":2631,"children":2632},{},[2633,2635,2640],{"type":49,"value":2634},"Use ",{"type":43,"tag":65,"props":2636,"children":2638},{"className":2637},[],[2639],{"type":49,"value":1802},{"type":49,"value":2641}," for .NET method name strings in JS — prevents typo bugs that silently fail",{"type":43,"tag":2515,"props":2643,"children":2644},{},[2645,2647,2653,2654],{"type":49,"value":2646},"Dispose ",{"type":43,"tag":65,"props":2648,"children":2650},{"className":2649},[],[2651],{"type":49,"value":2652},"DotNetObjectReference",{"type":49,"value":2551},{"type":43,"tag":65,"props":2655,"children":2657},{"className":2656},[],[2658],{"type":49,"value":2659},"DisposeAsync",{"type":43,"tag":52,"props":2661,"children":2663},{"id":2662},"_6-disposal-and-server-safety",[2664],{"type":49,"value":2665},"6. Disposal and Server Safety",{"type":43,"tag":59,"props":2667,"children":2668},{},[2669,2671,2677,2679,2685],{"type":49,"value":2670},"Always implement ",{"type":43,"tag":65,"props":2672,"children":2674},{"className":2673},[],[2675],{"type":49,"value":2676},"IAsyncDisposable",{"type":49,"value":2678},". Call JS cleanup first, then dispose references. Catch ",{"type":43,"tag":65,"props":2680,"children":2682},{"className":2681},[],[2683],{"type":49,"value":2684},"JSDisconnectedException",{"type":49,"value":2686}," for Blazor Server circuit loss:",{"type":43,"tag":98,"props":2688,"children":2690},{"className":343,"code":2689,"language":345,"meta":102,"style":102},"public async ValueTask DisposeAsync()\n{\n    try\n    {\n        if (_module is not null)\n        {\n            await _module.InvokeVoidAsync(\"dispose\");\n            await _module.DisposeAsync();\n        }\n    }\n    catch (JSDisconnectedException) { }\n\n    _dotNetRef?.Dispose();\n}\n",[2691],{"type":43,"tag":65,"props":2692,"children":2693},{"__ignoreMap":102},[2694,2702,2709,2717,2724,2732,2739,2747,2755,2762,2769,2777,2784,2792],{"type":43,"tag":108,"props":2695,"children":2696},{"class":110,"line":111},[2697],{"type":43,"tag":108,"props":2698,"children":2699},{},[2700],{"type":49,"value":2701},"public async ValueTask DisposeAsync()\n",{"type":43,"tag":108,"props":2703,"children":2704},{"class":110,"line":121},[2705],{"type":43,"tag":108,"props":2706,"children":2707},{},[2708],{"type":49,"value":382},{"type":43,"tag":108,"props":2710,"children":2711},{"class":110,"line":184},[2712],{"type":43,"tag":108,"props":2713,"children":2714},{},[2715],{"type":49,"value":2716},"    try\n",{"type":43,"tag":108,"props":2718,"children":2719},{"class":110,"line":226},[2720],{"type":43,"tag":108,"props":2721,"children":2722},{},[2723],{"type":49,"value":400},{"type":43,"tag":108,"props":2725,"children":2726},{"class":110,"line":385},[2727],{"type":43,"tag":108,"props":2728,"children":2729},{},[2730],{"type":49,"value":2731},"        if (_module is not null)\n",{"type":43,"tag":108,"props":2733,"children":2734},{"class":110,"line":394},[2735],{"type":43,"tag":108,"props":2736,"children":2737},{},[2738],{"type":49,"value":1428},{"type":43,"tag":108,"props":2740,"children":2741},{"class":110,"line":403},[2742],{"type":43,"tag":108,"props":2743,"children":2744},{},[2745],{"type":49,"value":2746},"            await _module.InvokeVoidAsync(\"dispose\");\n",{"type":43,"tag":108,"props":2748,"children":2749},{"class":110,"line":412},[2750],{"type":43,"tag":108,"props":2751,"children":2752},{},[2753],{"type":49,"value":2754},"            await _module.DisposeAsync();\n",{"type":43,"tag":108,"props":2756,"children":2757},{"class":110,"line":421},[2758],{"type":43,"tag":108,"props":2759,"children":2760},{},[2761],{"type":49,"value":1482},{"type":43,"tag":108,"props":2763,"children":2764},{"class":110,"line":430},[2765],{"type":43,"tag":108,"props":2766,"children":2767},{},[2768],{"type":49,"value":427},{"type":43,"tag":108,"props":2770,"children":2771},{"class":110,"line":545},[2772],{"type":43,"tag":108,"props":2773,"children":2774},{},[2775],{"type":49,"value":2776},"    catch (JSDisconnectedException) { }\n",{"type":43,"tag":108,"props":2778,"children":2779},{"class":110,"line":554},[2780],{"type":43,"tag":108,"props":2781,"children":2782},{"emptyLinePlaceholder":363},[2783],{"type":49,"value":366},{"type":43,"tag":108,"props":2785,"children":2786},{"class":110,"line":562},[2787],{"type":43,"tag":108,"props":2788,"children":2789},{},[2790],{"type":49,"value":2791},"    _dotNetRef?.Dispose();\n",{"type":43,"tag":108,"props":2793,"children":2794},{"class":110,"line":1278},[2795],{"type":43,"tag":108,"props":2796,"children":2797},{},[2798],{"type":49,"value":436},{"type":43,"tag":59,"props":2800,"children":2801},{},[2802,2804,2810,2812,2817,2819,2825],{"type":49,"value":2803},"Never use sync ",{"type":43,"tag":65,"props":2805,"children":2807},{"className":2806},[],[2808],{"type":49,"value":2809},"IDisposable",{"type":49,"value":2811}," for JS interop cleanup — ",{"type":43,"tag":65,"props":2813,"children":2815},{"className":2814},[],[2816],{"type":49,"value":338},{"type":49,"value":2818}," returns ",{"type":43,"tag":65,"props":2820,"children":2822},{"className":2821},[],[2823],{"type":49,"value":2824},"ValueTask",{"type":49,"value":2826}," and must be awaited.",{"type":43,"tag":52,"props":2828,"children":2830},{"id":2829},"_7-elementreference",[2831],{"type":49,"value":2832},"7. ElementReference",{"type":43,"tag":59,"props":2834,"children":2835},{},[2836,2838,2844],{"type":49,"value":2837},"Pass DOM elements via ",{"type":43,"tag":65,"props":2839,"children":2841},{"className":2840},[],[2842],{"type":49,"value":2843},"@ref",{"type":49,"value":2845},", not string IDs:",{"type":43,"tag":98,"props":2847,"children":2848},{"className":1515,"code":1552,"language":1517,"meta":102,"style":102},[2849],{"type":43,"tag":65,"props":2850,"children":2851},{"__ignoreMap":102},[2852],{"type":43,"tag":108,"props":2853,"children":2854},{"class":110,"line":111},[2855],{"type":43,"tag":108,"props":2856,"children":2857},{},[2858],{"type":49,"value":1552},{"type":43,"tag":98,"props":2860,"children":2862},{"className":343,"code":2861,"language":345,"meta":102,"style":102},"await _chart.InitializeAsync(_canvasRef);\n",[2863],{"type":43,"tag":65,"props":2864,"children":2865},{"__ignoreMap":102},[2866],{"type":43,"tag":108,"props":2867,"children":2868},{"class":110,"line":111},[2869],{"type":43,"tag":108,"props":2870,"children":2871},{},[2872],{"type":49,"value":2861},{"type":43,"tag":52,"props":2874,"children":2876},{"id":2875},"checklist",[2877],{"type":49,"value":2878},"Checklist",{"type":43,"tag":2511,"props":2880,"children":2883},{"className":2881},[2882],"contains-task-list",[2884,2916,2932,2953,2986,3014,3029,3045],{"type":43,"tag":2515,"props":2885,"children":2888},{"className":2886},[2887],"task-list-item",[2889,2894,2896,2901,2902,2907,2909,2914],{"type":43,"tag":2890,"props":2891,"children":2893},"input",{"disabled":363,"type":2892},"checkbox",[],{"type":49,"value":2895}," JS is in collocated ",{"type":43,"tag":65,"props":2897,"children":2899},{"className":2898},[],[2900],{"type":49,"value":70},{"type":49,"value":1771},{"type":43,"tag":65,"props":2903,"children":2905},{"className":2904},[],[2906],{"type":49,"value":78},{"type":49,"value":2908}," — no ",{"type":43,"tag":65,"props":2910,"children":2912},{"className":2911},[],[2913],{"type":49,"value":86},{"type":49,"value":2915}," globals",{"type":43,"tag":2515,"props":2917,"children":2919},{"className":2918},[2887],[2920,2923,2925,2930],{"type":43,"tag":2890,"props":2921,"children":2922},{"disabled":363,"type":2892},[],{"type":49,"value":2924}," All interop in ",{"type":43,"tag":65,"props":2926,"children":2928},{"className":2927},[],[2929],{"type":49,"value":299},{"type":49,"value":2931}," or event handlers — never during prerender",{"type":43,"tag":2515,"props":2933,"children":2935},{"className":2934},[2887],[2936,2939,2941,2946,2948],{"type":43,"tag":2890,"props":2937,"children":2938},{"disabled":363,"type":2892},[],{"type":49,"value":2940}," ",{"type":43,"tag":65,"props":2942,"children":2944},{"className":2943},[],[2945],{"type":49,"value":2676},{"type":49,"value":2947}," catches ",{"type":43,"tag":65,"props":2949,"children":2951},{"className":2950},[],[2952],{"type":49,"value":2684},{"type":43,"tag":2515,"props":2954,"children":2956},{"className":2955},[2887],[2957,2960,2961,2966,2968,2973,2975,2980,2981],{"type":43,"tag":2890,"props":2958,"children":2959},{"disabled":363,"type":2892},[],{"type":49,"value":2940},{"type":43,"tag":65,"props":2962,"children":2964},{"className":2963},[],[2965],{"type":49,"value":2652},{"type":49,"value":2967}," disposed in ",{"type":43,"tag":65,"props":2969,"children":2971},{"className":2970},[],[2972],{"type":49,"value":2659},{"type":49,"value":2974},"; JS side has ",{"type":43,"tag":65,"props":2976,"children":2978},{"className":2977},[],[2979],{"type":49,"value":1777},{"type":49,"value":2622},{"type":43,"tag":65,"props":2982,"children":2984},{"className":2983},[],[2985],{"type":49,"value":981},{"type":43,"tag":2515,"props":2987,"children":2989},{"className":2988},[2887],[2990,2993,2994,2999,3001,3006,3008],{"type":43,"tag":2890,"props":2991,"children":2992},{"disabled":363,"type":2892},[],{"type":49,"value":2940},{"type":43,"tag":65,"props":2995,"children":2997},{"className":2996},[],[2998],{"type":49,"value":2523},{"type":49,"value":3000}," methods are ",{"type":43,"tag":65,"props":3002,"children":3004},{"className":3003},[],[3005],{"type":49,"value":2536},{"type":49,"value":3007}," and use ",{"type":43,"tag":65,"props":3009,"children":3011},{"className":3010},[],[3012],{"type":49,"value":3013},"await InvokeAsync(StateHasChanged)",{"type":43,"tag":2515,"props":3015,"children":3017},{"className":3016},[2887],[3018,3021,3022,3027],{"type":43,"tag":2890,"props":3019,"children":3020},{"disabled":363,"type":2892},[],{"type":49,"value":2940},{"type":43,"tag":65,"props":3023,"children":3025},{"className":3024},[],[3026],{"type":49,"value":338},{"type":49,"value":3028}," used when no return value is needed",{"type":43,"tag":2515,"props":3030,"children":3032},{"className":3031},[2887],[3033,3036,3037,3043],{"type":43,"tag":2890,"props":3034,"children":3035},{"disabled":363,"type":2892},[],{"type":49,"value":2940},{"type":43,"tag":65,"props":3038,"children":3040},{"className":3039},[],[3041],{"type":49,"value":3042},"ElementReference",{"type":49,"value":3044}," instead of string IDs",{"type":43,"tag":2515,"props":3046,"children":3048},{"className":3047},[2887],[3049,3052],{"type":43,"tag":2890,"props":3050,"children":3051},{"disabled":363,"type":2892},[],{"type":49,"value":3053}," Related operations batched into single interop calls (both .NET→JS and JS→.NET)",{"type":43,"tag":52,"props":3055,"children":3057},{"id":3056},"common-mistakes-checklist",[3058],{"type":49,"value":3059},"Common Mistakes Checklist",{"type":43,"tag":3061,"props":3062,"children":3063},"table",{},[3064,3083],{"type":43,"tag":3065,"props":3066,"children":3067},"thead",{},[3068],{"type":43,"tag":3069,"props":3070,"children":3071},"tr",{},[3072,3078],{"type":43,"tag":3073,"props":3074,"children":3075},"th",{},[3076],{"type":49,"value":3077},"Mistake",{"type":43,"tag":3073,"props":3079,"children":3080},{},[3081],{"type":49,"value":3082},"Fix",{"type":43,"tag":3084,"props":3085,"children":3086},"tbody",{},[3087,3109,3122,3135,3156,3176,3201,3224,3252,3283,3306,3331,3356,3386,3412,3439,3465,3490],{"type":43,"tag":3069,"props":3088,"children":3089},{},[3090,3096],{"type":43,"tag":3091,"props":3092,"children":3093},"td",{},[3094],{"type":49,"value":3095},"Using JS for something achievable with CSS",{"type":43,"tag":3091,"props":3097,"children":3098},{},[3099,3101,3107],{"type":49,"value":3100},"Use CSS custom properties, ",{"type":43,"tag":65,"props":3102,"children":3104},{"className":3103},[],[3105],{"type":49,"value":3106},"data-",{"type":49,"value":3108}," attributes, pseudo-classes",{"type":43,"tag":3069,"props":3110,"children":3111},{},[3112,3117],{"type":43,"tag":3091,"props":3113,"children":3114},{},[3115],{"type":49,"value":3116},"Many fine-grained interop calls",{"type":43,"tag":3091,"props":3118,"children":3119},{},[3120],{"type":49,"value":3121},"Batch into coarse functions — both .NET→JS and JS→.NET",{"type":43,"tag":3069,"props":3123,"children":3124},{},[3125,3130],{"type":43,"tag":3091,"props":3126,"children":3127},{},[3128],{"type":49,"value":3129},"Component imports JS module directly",{"type":43,"tag":3091,"props":3131,"children":3132},{},[3133],{"type":49,"value":3134},"Encapsulate in a strongly typed interop class",{"type":43,"tag":3069,"props":3136,"children":3137},{},[3138,3143],{"type":43,"tag":3091,"props":3139,"children":3140},{},[3141],{"type":49,"value":3142},"Magic strings for method names \u002F module paths",{"type":43,"tag":3091,"props":3144,"children":3145},{},[3146,3148,3154],{"type":49,"value":3147},"Define ",{"type":43,"tag":65,"props":3149,"children":3151},{"className":3150},[],[3152],{"type":49,"value":3153},"internal const",{"type":49,"value":3155}," fields in the interop class",{"type":43,"tag":3069,"props":3157,"children":3158},{},[3159,3164],{"type":43,"tag":3091,"props":3160,"children":3161},{},[3162],{"type":49,"value":3163},"Interface + implementation for interop wrapper",{"type":43,"tag":3091,"props":3165,"children":3166},{},[3167,3169,3174],{"type":49,"value":3168},"Use a plain class; mock ",{"type":43,"tag":65,"props":3170,"children":3172},{"className":3171},[],[3173],{"type":49,"value":1713},{"type":49,"value":3175}," for tests instead",{"type":43,"tag":3069,"props":3177,"children":3178},{},[3179,3190],{"type":43,"tag":3091,"props":3180,"children":3181},{},[3182,3184],{"type":49,"value":3183},"JS calls in ",{"type":43,"tag":65,"props":3185,"children":3187},{"className":3186},[],[3188],{"type":49,"value":3189},"OnInitializedAsync",{"type":43,"tag":3091,"props":3191,"children":3192},{},[3193,3195],{"type":49,"value":3194},"Move to ",{"type":43,"tag":65,"props":3196,"children":3198},{"className":3197},[],[3199],{"type":49,"value":3200},"OnAfterRenderAsync(firstRender)",{"type":43,"tag":3069,"props":3202,"children":3203},{},[3204,3215],{"type":43,"tag":3091,"props":3205,"children":3206},{},[3207,3213],{"type":43,"tag":65,"props":3208,"children":3210},{"className":3209},[],[3211],{"type":49,"value":3212},"InvokeAsync\u003Cobject>",{"type":49,"value":3214}," for void calls",{"type":43,"tag":3091,"props":3216,"children":3217},{},[3218,3219],{"type":49,"value":2634},{"type":43,"tag":65,"props":3220,"children":3222},{"className":3221},[],[3223],{"type":49,"value":338},{"type":43,"tag":3069,"props":3225,"children":3226},{},[3227,3237],{"type":43,"tag":3091,"props":3228,"children":3229},{},[3230,3235],{"type":43,"tag":65,"props":3231,"children":3233},{"className":3232},[],[3234],{"type":49,"value":2809},{"type":49,"value":3236}," with fire-and-forget JS",{"type":43,"tag":3091,"props":3238,"children":3239},{},[3240,3241,3246,3247],{"type":49,"value":2634},{"type":43,"tag":65,"props":3242,"children":3244},{"className":3243},[],[3245],{"type":49,"value":2676},{"type":49,"value":1771},{"type":43,"tag":65,"props":3248,"children":3250},{"className":3249},[],[3251],{"type":49,"value":1006},{"type":43,"tag":3069,"props":3253,"children":3254},{},[3255,3267],{"type":43,"tag":3091,"props":3256,"children":3257},{},[3258,3260,3265],{"type":49,"value":3259},"Global ",{"type":43,"tag":65,"props":3261,"children":3263},{"className":3262},[],[3264],{"type":49,"value":86},{"type":49,"value":3266}," JS functions",{"type":43,"tag":3091,"props":3268,"children":3269},{},[3270,3272,3277,3278],{"type":49,"value":3271},"Use collocated ",{"type":43,"tag":65,"props":3273,"children":3275},{"className":3274},[],[3276],{"type":49,"value":70},{"type":49,"value":1771},{"type":43,"tag":65,"props":3279,"children":3281},{"className":3280},[],[3282],{"type":49,"value":78},{"type":43,"tag":3069,"props":3284,"children":3285},{},[3286,3291],{"type":43,"tag":3091,"props":3287,"children":3288},{},[3289],{"type":49,"value":3290},"String element IDs passed to JS",{"type":43,"tag":3091,"props":3292,"children":3293},{},[3294,3295,3300,3301],{"type":49,"value":2634},{"type":43,"tag":65,"props":3296,"children":3298},{"className":3297},[],[3299],{"type":49,"value":3042},{"type":49,"value":1771},{"type":43,"tag":65,"props":3302,"children":3304},{"className":3303},[],[3305],{"type":49,"value":2843},{"type":43,"tag":3069,"props":3307,"children":3308},{},[3309,3319],{"type":43,"tag":3091,"props":3310,"children":3311},{},[3312,3317],{"type":43,"tag":65,"props":3313,"children":3315},{"className":3314},[],[3316],{"type":49,"value":2523},{"type":49,"value":3318}," on private method",{"type":43,"tag":3091,"props":3320,"children":3321},{},[3322,3324,3329],{"type":49,"value":3323},"Must be ",{"type":43,"tag":65,"props":3325,"children":3327},{"className":3326},[],[3328],{"type":49,"value":2536},{"type":49,"value":3330}," — silently fails otherwise",{"type":43,"tag":3069,"props":3332,"children":3333},{},[3334,3344],{"type":43,"tag":3091,"props":3335,"children":3336},{},[3337,3342],{"type":43,"tag":65,"props":3338,"children":3340},{"className":3339},[],[3341],{"type":49,"value":2652},{"type":49,"value":3343}," not disposed",{"type":43,"tag":3091,"props":3345,"children":3346},{},[3347,3349,3354],{"type":49,"value":3348},"Dispose in ",{"type":43,"tag":65,"props":3350,"children":3352},{"className":3351},[],[3353],{"type":49,"value":2659},{"type":49,"value":3355}," — causes memory leak",{"type":43,"tag":3069,"props":3357,"children":3358},{},[3359,3375],{"type":43,"tag":3091,"props":3360,"children":3361},{},[3362,3368,3370],{"type":43,"tag":65,"props":3363,"children":3365},{"className":3364},[],[3366],{"type":49,"value":3367},"StateHasChanged()",{"type":49,"value":3369}," without ",{"type":43,"tag":65,"props":3371,"children":3373},{"className":3372},[],[3374],{"type":49,"value":330},{"type":43,"tag":3091,"props":3376,"children":3377},{},[3378,3380],{"type":49,"value":3379},"Wrap in ",{"type":43,"tag":65,"props":3381,"children":3383},{"className":3382},[],[3384],{"type":49,"value":3385},"await InvokeAsync(() => { StateHasChanged(); })",{"type":43,"tag":3069,"props":3387,"children":3388},{},[3389,3401],{"type":43,"tag":3091,"props":3390,"children":3391},{},[3392,3394,3399],{"type":49,"value":3393},"JS ",{"type":43,"tag":65,"props":3395,"children":3397},{"className":3396},[],[3398],{"type":49,"value":981},{"type":49,"value":3400}," without error handling",{"type":43,"tag":3091,"props":3402,"children":3403},{},[3404,3405,3410],{"type":49,"value":3379},{"type":43,"tag":65,"props":3406,"children":3408},{"className":3407},[],[3409],{"type":49,"value":1777},{"type":49,"value":3411}," — circuit loss throws",{"type":43,"tag":3069,"props":3413,"children":3414},{},[3415,3427],{"type":43,"tag":3091,"props":3416,"children":3417},{},[3418,3420,3425],{"type":49,"value":3419},"Bare ",{"type":43,"tag":65,"props":3421,"children":3423},{"className":3422},[],[3424],{"type":49,"value":1755},{"type":49,"value":3426}," in JS event handlers",{"type":43,"tag":3091,"props":3428,"children":3429},{},[3430,3432,3437],{"type":49,"value":3431},"Wrap in a class with ",{"type":43,"tag":65,"props":3433,"children":3435},{"className":3434},[],[3436],{"type":49,"value":1923},{"type":49,"value":3438}," private field",{"type":43,"tag":3069,"props":3440,"children":3441},{},[3442,3454],{"type":43,"tag":3091,"props":3443,"children":3444},{},[3445,3447,3452],{"type":49,"value":3446},"Magic strings in JS ",{"type":43,"tag":65,"props":3448,"children":3450},{"className":3449},[],[3451],{"type":49,"value":981},{"type":49,"value":3453}," calls",{"type":43,"tag":3091,"props":3455,"children":3456},{},[3457,3458,3463],{"type":49,"value":2634},{"type":43,"tag":65,"props":3459,"children":3461},{"className":3460},[],[3462],{"type":49,"value":1802},{"type":49,"value":3464}," at module top — typos silently fail at runtime",{"type":43,"tag":3069,"props":3466,"children":3467},{},[3468,3478],{"type":43,"tag":3091,"props":3469,"children":3470},{},[3471,3472],{"type":49,"value":3183},{"type":43,"tag":65,"props":3473,"children":3475},{"className":3474},[],[3476],{"type":49,"value":3477},"OnParametersSetAsync",{"type":43,"tag":3091,"props":3479,"children":3480},{},[3481,3483,3488],{"type":49,"value":3482},"Track changes, apply in ",{"type":43,"tag":65,"props":3484,"children":3486},{"className":3485},[],[3487],{"type":49,"value":299},{"type":49,"value":3489}," with guard",{"type":43,"tag":3069,"props":3491,"children":3492},{},[3493,3498],{"type":43,"tag":3091,"props":3494,"children":3495},{},[3496],{"type":49,"value":3497},"No null check before calling module",{"type":43,"tag":3091,"props":3499,"children":3500},{},[3501,3503,3509],{"type":49,"value":3502},"Check ",{"type":43,"tag":65,"props":3504,"children":3506},{"className":3505},[],[3507],{"type":49,"value":3508},"module is not null",{"type":49,"value":3510}," before use",{"type":43,"tag":3512,"props":3513,"children":3514},"style",{},[3515],{"type":49,"value":3516},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3518,"total":3624},[3519,3536,3551,3569,3583,3600,3610],{"slug":3520,"name":3520,"fn":3521,"description":3522,"org":3523,"tags":3524,"stars":25,"repoUrl":26,"updatedAt":3535},"analyzing-dotnet-performance","analyze .NET code for performance anti-patterns","Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I\u002FO with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3525,3526,3529,3532],{"name":13,"slug":14,"type":15},{"name":3527,"slug":3528,"type":15},"Code Analysis","code-analysis",{"name":3530,"slug":3531,"type":15},"Debugging","debugging",{"name":3533,"slug":3534,"type":15},"Performance","performance","2026-07-12T08:23:25.400375",{"slug":3537,"name":3537,"fn":3538,"description":3539,"org":3540,"tags":3541,"stars":25,"repoUrl":26,"updatedAt":3550},"android-tombstone-symbolication","symbolicate .NET runtime frames in Android tombstones","Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java\u002FKotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3542,3543,3546,3547],{"name":13,"slug":14,"type":15},{"name":3544,"slug":3545,"type":15},"Android","android",{"name":3530,"slug":3531,"type":15},{"name":3548,"slug":3549,"type":15},"Microsoft","microsoft","2026-07-12T08:23:21.595572",{"slug":3552,"name":3552,"fn":3553,"description":3554,"org":3555,"tags":3556,"stars":25,"repoUrl":26,"updatedAt":3568},"apple-crash-symbolication","symbolicate .NET runtime frames in crash logs","Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift\u002FObjective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3557,3558,3559,3562,3565],{"name":13,"slug":14,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3560,"slug":3561,"type":15},"iOS","ios",{"name":3563,"slug":3564,"type":15},"macOS","macos",{"name":3566,"slug":3567,"type":15},"Observability","observability","2026-07-12T08:23:20.369986",{"slug":3570,"name":3570,"fn":3571,"description":3572,"org":3573,"tags":3574,"stars":25,"repoUrl":26,"updatedAt":3582},"assertion-quality","evaluate assertion quality in test suites","Analyzes the variety and depth of assertions across test suites in any language. Use when the user asks to evaluate assertion quality, find shallow tests, identify assertion-free tests (no assertions or only trivial ones like Assert.IsNotNull \u002F toBeTruthy()), flag self-referential or tautological assertions, measure assertion diversity, or audit whether tests verify different facets of behavior. Polyglot: .NET, Python, TS\u002FJS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. DO NOT USE FOR: writing new tests (use code-testing-agent \u002F writing-mstest-tests), mutation reasoning about whether tests would catch a bug (use test-gap-analysis), or a general severity-ranked anti-pattern audit (use test-anti-patterns), fixing or rewriting assertions, or writing, fixing, or modernizing MSTest tests, assertions, or attributes (use writing-mstest-tests).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3575,3576,3579],{"name":3527,"slug":3528,"type":15},{"name":3577,"slug":3578,"type":15},"QA","qa",{"name":3580,"slug":3581,"type":15},"Testing","testing","2026-07-12T08:23:51.277743",{"slug":3584,"name":3584,"fn":3585,"description":3586,"org":3587,"tags":3588,"stars":25,"repoUrl":26,"updatedAt":3599},"author-component","create and review Blazor components","Create or review Blazor components (.razor files) with correct architecture. USE FOR: writing new Blazor components that do NOT involve JavaScript interop, implementing parameters and EventCallback, RenderFragment slots, component lifecycle (OnInitializedAsync, OnParametersSet), async patterns, IAsyncDisposable, CancellationToken, CSS isolation, code-behind. DO NOT USE FOR: creating new projects (use create-blazor-project), JavaScript interop or calling browser APIs from Blazor (use use-js-interop), forms and validation (use collect-user-input), prerendering issues (use support-prerendering), HTTP data fetching patterns (use fetch-and-send-data), coordinating state between unrelated components (use coordinate-components).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3589,3590,3591,3593,3596],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":3592,"slug":345,"type":15},"C#",{"name":3594,"slug":3595,"type":15},"UI Components","ui-components",{"name":3597,"slug":3598,"type":15},"Web Development","web-development","2026-07-15T06:03:29.216359",{"slug":3601,"name":3601,"fn":3602,"description":3603,"org":3604,"tags":3605,"stars":25,"repoUrl":26,"updatedAt":3609},"binlog-failure-analysis","analyze MSBuild binary logs","Analyze MSBuild binary logs to diagnose build failures. USE FOR: build errors that are unclear from console output, diagnosing cascading failures across multi-project builds, tracing MSBuild target execution order, and generally any MSBuild build issues. Requires an existing .binlog file. DO NOT USE FOR: generating binlogs (use binlog-generation), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3606,3607,3608],{"name":3527,"slug":3528,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3548,"slug":3549,"type":15},"2026-07-12T08:21:34.637923",{"slug":3611,"name":3611,"fn":3612,"description":3613,"org":3614,"tags":3615,"stars":25,"repoUrl":26,"updatedAt":3623},"binlog-generation","generate MSBuild binary logs for diagnostics","Generate MSBuild binary logs (binlogs) for build diagnostics and analysis. USE FOR: adding \u002Fbl:{} to any dotnet build, test, pack, publish, or restore command to capture a full build execution trace, prerequisite for binlog-failure-analysis and build-perf-diagnostics skills, enabling post-build investigation of errors or performance. Requires MSBuild 17.8+ \u002F .NET 8 SDK+ for {} placeholder; PowerShell needs -bl:{{}}. DO NOT USE FOR: non-MSBuild build systems (npm, Maven, CMake), analyzing an existing binlog (use binlog-failure-analysis instead).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3616,3619,3620],{"name":3617,"slug":3618,"type":15},"Build","build",{"name":3530,"slug":3531,"type":15},{"name":3621,"slug":3622,"type":15},"Engineering","engineering","2026-07-19T05:38:19.340791",96,{"items":3626,"total":3731},[3627,3639,3646,3653,3661,3667,3675,3681,3687,3697,3710,3721],{"slug":3628,"name":3628,"fn":3629,"description":3630,"org":3631,"tags":3632,"stars":3636,"repoUrl":3637,"updatedAt":3638},"multithreaded-task-migration","migrate MSBuild tasks to multithreaded mode","Guide for migrating MSBuild tasks to multithreaded mode support, including compatibility red-team review. Use this when converting tasks to thread-safe versions, implementing IMultiThreadableTask, adding TaskEnvironment support, or auditing migrations for behavioral compatibility.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3633,3634,3635],{"name":13,"slug":14,"type":15},{"name":3621,"slug":3622,"type":15},{"name":3533,"slug":3534,"type":15},5535,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmsbuild","2026-07-22T05:37:33.965588",{"slug":3520,"name":3520,"fn":3521,"description":3522,"org":3640,"tags":3641,"stars":25,"repoUrl":26,"updatedAt":3535},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3642,3643,3644,3645],{"name":13,"slug":14,"type":15},{"name":3527,"slug":3528,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3533,"slug":3534,"type":15},{"slug":3537,"name":3537,"fn":3538,"description":3539,"org":3647,"tags":3648,"stars":25,"repoUrl":26,"updatedAt":3550},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3649,3650,3651,3652],{"name":13,"slug":14,"type":15},{"name":3544,"slug":3545,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3548,"slug":3549,"type":15},{"slug":3552,"name":3552,"fn":3553,"description":3554,"org":3654,"tags":3655,"stars":25,"repoUrl":26,"updatedAt":3568},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3656,3657,3658,3659,3660],{"name":13,"slug":14,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3560,"slug":3561,"type":15},{"name":3563,"slug":3564,"type":15},{"name":3566,"slug":3567,"type":15},{"slug":3570,"name":3570,"fn":3571,"description":3572,"org":3662,"tags":3663,"stars":25,"repoUrl":26,"updatedAt":3582},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3664,3665,3666],{"name":3527,"slug":3528,"type":15},{"name":3577,"slug":3578,"type":15},{"name":3580,"slug":3581,"type":15},{"slug":3584,"name":3584,"fn":3585,"description":3586,"org":3668,"tags":3669,"stars":25,"repoUrl":26,"updatedAt":3599},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3670,3671,3672,3673,3674],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":3592,"slug":345,"type":15},{"name":3594,"slug":3595,"type":15},{"name":3597,"slug":3598,"type":15},{"slug":3601,"name":3601,"fn":3602,"description":3603,"org":3676,"tags":3677,"stars":25,"repoUrl":26,"updatedAt":3609},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3678,3679,3680],{"name":3527,"slug":3528,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3548,"slug":3549,"type":15},{"slug":3611,"name":3611,"fn":3612,"description":3613,"org":3682,"tags":3683,"stars":25,"repoUrl":26,"updatedAt":3623},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3684,3685,3686],{"name":3617,"slug":3618,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3621,"slug":3622,"type":15},{"slug":3688,"name":3688,"fn":3689,"description":3690,"org":3691,"tags":3692,"stars":25,"repoUrl":26,"updatedAt":3696},"build-parallelism","optimize MSBuild build parallelism","Diagnose and fix under-parallelized MSBuild builds. USE WHEN a multi-project solution build is slower than expected, doesn't speed up when you add cores, pegs a single core while others idle, or you want to know why `-m` isn't helping. Note: `\u002Fmaxcpucount` default is 1 (sequential) — always pass `-m` for parallel builds. Covers finding the critical path (longest serial ProjectReference chain), graph build (`\u002Fgraph`), BuildInParallel, and solution filters (`.slnf`). DO NOT USE FOR: single-project builds, incremental issues (use incremental-build), compilation slowness inside one project (use build-perf-diagnostics), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3693,3694,3695],{"name":13,"slug":14,"type":15},{"name":3621,"slug":3622,"type":15},{"name":3533,"slug":3534,"type":15},"2026-07-19T05:38:18.364937",{"slug":3698,"name":3698,"fn":3699,"description":3700,"org":3701,"tags":3702,"stars":25,"repoUrl":26,"updatedAt":3709},"build-perf-baseline","establish and optimize build performance baselines","Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before\u002Fafter measurements (cold, warm, no-op scenarios), applying optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3703,3704,3707,3708],{"name":3621,"slug":3622,"type":15},{"name":3705,"slug":3706,"type":15},"Monitoring","monitoring",{"name":3533,"slug":3534,"type":15},{"name":3580,"slug":3581,"type":15},"2026-07-12T08:21:35.865649",{"slug":3711,"name":3711,"fn":3712,"description":3713,"org":3714,"tags":3715,"stars":25,"repoUrl":26,"updatedAt":3720},"build-perf-diagnostics","diagnose MSBuild build performance bottlenecks","Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target\u002FTask Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3716,3717,3718,3719],{"name":13,"slug":14,"type":15},{"name":3530,"slug":3531,"type":15},{"name":3621,"slug":3622,"type":15},{"name":3533,"slug":3534,"type":15},"2026-07-12T08:21:40.961722",{"slug":3722,"name":3722,"fn":3723,"description":3724,"org":3725,"tags":3726,"stars":25,"repoUrl":26,"updatedAt":3730},"check-bin-obj-clash","detect MSBuild output path conflicts","Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing\u002Foverwritten outputs in multi-project or multi-targeting builds where bin\u002Fobj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3727,3728,3729],{"name":3530,"slug":3531,"type":15},{"name":3621,"slug":3622,"type":15},{"name":3577,"slug":3578,"type":15},"2026-07-19T05:38:14.336279",144]