Files
2026-07-10 02:19:48 +00:00

181 lines
9.2 KiB
YAML

name: use-js-interop
description: Evaluates the dotnet-blazor/use-js-interop skill
type: capability
config:
timeout: 10m
stimuli:
- name: Auto-saving notepad that survives page reloads
prompt: |
I need a `Notepad` component for my Blazor Web App.
Requirements:
- A textarea where the user can type notes
- Content is automatically saved to the browser every 30 seconds
- When the user returns to the page (or reloads), their last saved content is restored
- A "Last saved at HH:MM:SS" label that updates after each save
- If the user has unsaved changes and tries to close the browser tab, show the browser's
built-in "Leave site?" confirmation dialog
- A "Clear" button that erases both the textarea and the saved data
- A "Save now" button for manual saves
environment:
commands:
- dotnet new blazor --interactivity Server --no-https
graders:
- type: output-contains
config:
substring: OnAfterRenderAsync
- type: output-contains
config:
substring: IAsyncDisposable
- type: output-matches
config:
pattern: (export function|export \{)
- type: prompt
rubric:
- Uses a collocated .razor.js file with exported functions — not a global script tag or window-level globals
- Initializes interop in OnAfterRenderAsync(firstRender), not in OnInitializedAsync — JS is unavailable during prerendering
- Restores saved content on first interactive render so the user sees their previous notes immediately
- Batches related browser operations into single JS calls where possible — e.g., save content + timestamp together instead of separate interop calls for each
- Registers and removes the beforeunload listener properly — adds on setup, removes on dispose
- Manages the auto-save interval in JS and clears it on dispose — no dangling timers
- Implements IAsyncDisposable and catches JSDisconnectedException so disposal doesn't throw when the circuit is already gone
- Uses InvokeVoidAsync when no return value is needed
- Does not use fire-and-forget (unawaited ValueTask) — all JS calls are properly awaited
- The clear function removes stored data and resets the beforeunload guard in one operation
- Uses sessionStorage (or localStorage) for persistence, the beforeunload event for the unsaved-changes prompt, and setInterval/clearInterval for the auto-save timer
- name: User activity tracker that detects idle timeout
prompt: |
I need a `UserActivityTracker` component for my Blazor Web App.
Requirements:
- Monitors user activity on the page (mouse movement, key presses, clicks, scrolling)
- After a configurable period of inactivity, triggers an `EventCallback OnIdle` to notify the parent
- When the user becomes active again after being idle, triggers `EventCallback OnActive`
- The idle timeout duration is set via `[Parameter] int TimeoutSeconds` (default 300)
- Wraps child content — can be placed around any section of the app
- After the component is removed from the page, no leftover timers or event handlers should remain
environment:
commands:
- dotnet new blazor --interactivity Server --no-https
graders:
- type: output-contains
config:
substring: OnAfterRenderAsync
- type: output-contains
config:
substring: DotNetObjectReference
- type: output-contains
config:
substring: IAsyncDisposable
- type: output-matches
config:
pattern: (export function|export \{)
- type: prompt
rubric:
- Uses a collocated .razor.js file with exported functions — not global window functions or inline scripts
- Registers activity event listeners (mousemove, keydown, click, scroll) in OnAfterRenderAsync(firstRender) — not during prerendering
- Uses DotNetObjectReference so JS can call back into .NET for idle/active transitions and disposes it in DisposeAsync
- Implements IAsyncDisposable and catches JSDisconnectedException in DisposeAsync
- Manages the idle timeout timer in JavaScript using setTimeout/clearTimeout — not via .NET Task.Delay
- Cleans up all event listeners and timers in the JS dispose function — no dangling handlers after component removal
- name: Responsive layout that adapts to screen size
prompt: |
My Blazor Web App needs to adapt its layout based on the browser viewport width. I need:
1. A `ScreenSizeProvider` component that wraps child content and detects the current
viewport category:
- `Mobile` (width < 768px)
- `Tablet` (768px ≤ width < 1024px)
- `Desktop` (width ≥ 1024px)
2. The current category should be available to all child components as a cascading value
3. When the user resizes their browser, the category should update in real time
4. The default category (before detection runs) should be `Desktop`
Usage:
```razor
<ScreenSizeProvider>
<MyNavigation /> @* can read the cascading ScreenSize value *@
<MyContent />
</ScreenSizeProvider>
```
environment:
commands:
- dotnet new blazor --interactivity Server --no-https
graders:
- type: output-contains
config:
substring: CascadingValue
- type: output-contains
config:
substring: OnAfterRenderAsync
- type: output-contains
config:
substring: IAsyncDisposable
- type: output-matches
config:
pattern: (export function|export \{)
- type: prompt
rubric:
- Provides the screen size category to children via CascadingValue — child components can consume it with [CascadingParameter]
- Uses a collocated .razor.js module with exported functions — not global scripts
- Sets up matchMedia listeners or resize listener in OnAfterRenderAsync(firstRender) — not in OnInitializedAsync
- Defaults to Desktop (or a sensible fallback) during prerendering before JavaScript is available
- Updates the category in real time when the browser is resized — uses event-based detection, not polling
- Uses DotNetObjectReference for the JS resize/matchMedia callback and disposes it in DisposeAsync
- The [JSInvokable] callback wraps state changes in InvokeAsync(StateHasChanged)
- Implements IAsyncDisposable and catches JSDisconnectedException in DisposeAsync
- Removes matchMedia/resize event listeners in the JS dispose function — no dangling listeners
- Does not call JS interop during prerendering — guards all JS calls behind firstRender or a flag
- Uses window.matchMedia or the resize event for viewport detection — not polling or server-side user-agent sniffing
- name: Infinite scroll list using IntersectionObserver
prompt: |
I need an `InfiniteScrollList` component for my Blazor Web App that loads more items
as the user scrolls.
Requirements:
- Displays items in a scrollable container
- When the user scrolls near the bottom, automatically triggers loading of more items
- Shows a loading spinner while fetching
- Stops triggering when there are no more items (parent signals this via a parameter)
- The component is generic — it accepts a `RenderFragment<TItem>` for rendering each item
- Uses an efficient browser API for scroll detection (not scroll event listeners)
Usage:
```razor
<InfiniteScrollList Items="products" HasMore="hasMore"
OnLoadMore="LoadNextPage" Context="product">
<div class="product-card">@product.Name — @product.Price</div>
</InfiniteScrollList>
```
environment:
commands:
- dotnet new blazor --interactivity Server --no-https
graders:
- type: output-contains
config:
substring: IntersectionObserver
- type: output-contains
config:
substring: OnAfterRenderAsync
- type: output-contains
config:
substring: IAsyncDisposable
- type: output-contains
config:
substring: DotNetObjectReference
- type: prompt
rubric:
- Uses a collocated .razor.js file with exported functions — not a global script
- Creates IntersectionObserver in OnAfterRenderAsync(firstRender) — not during prerendering
- Uses a sentinel element (observed by IntersectionObserver) at the bottom of the list to detect when to load more
- Uses DotNetObjectReference to receive the intersection callback from JS and disposes it in DisposeAsync
- The [JSInvokable] callback is public and wraps state changes in InvokeAsync(StateHasChanged)
- Calls observer.disconnect() in the JS dispose function — no dangling observers
- Implements IAsyncDisposable and catches JSDisconnectedException in DisposeAsync
- Uses ElementReference for the sentinel element — not a string ID
- Respects the HasMore parameter — does not trigger loads or observe the sentinel when there are no more items
- Uses IntersectionObserver (not scroll event listeners) for efficient visibility detection