Blazing story addon
Collection of agent skills for Blazing Story, enabling AI coding assistants to implement stories and addons in Blazing Story projects.
npx -y skills add BlazingStory/agent-skills --skill blazing-story-addonAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Implement a custom addon for a Blazing Story application. Use when the user asks to create, add, or implement an addon with specific functionality — such as toolbar buttons, panel tabs, or preview decorators — in a Blazing Story (.NET / Blazor / Storybook) project.
The file declares its own license as Unlicense. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
14.4 KB, as published. Nobody here has run it
Blazing Story — Addon Implementation
Create and register a custom addon in the currently open Blazing Story project.
Investigation policy
The main goal of this policy is to free the developer from the hassle of approving "may I run this command?" prompts one by one. Many of those prompts come from operations that poke around outside the project — and most of the knowledge needed to build an addon is already available without them.
Implement the addon relying primarily on:
- The guidance in this skill file
- Your own knowledge of C#, .NET, Blazor, and general web/UI development
- Other relevant skills available in this environment
- Already-configured MCP servers and tools
- Read-only exploration of the current project (
ls,Glob,Grep,Read)
Avoid operations that inspect the NuGet package cache folder, decompile Blazing Story DLLs, or otherwise probe the installed package contents. These are slow, require the developer's per-command approval, and disrupt the flow of work.
If implementation details that are not covered above become necessary, consult the published source code on GitHub at https://github.com/jsakamoto/BlazingStory instead of digging into the local NuGet cache or decompiling DLLs.
This policy may be relaxed only when strictly unavoidable.
Step 1: Understand the requirements
From $ARGUMENTS or the user's message, identify:
- What UI to add: toolbar button/toggle, popup menu, panel tab, preview decorator, or a combination
- What behavior is needed: toggle state, CSS injection, JavaScript invocation, panel content, etc.
- Whether toolbar ↔ decorator communication is needed (i.e., toolbar action affects the preview frame)
Step 2: Locate the stories project
Find the stories project (typically *.Stories/) and its App.razor or equivalent file that contains <BlazingStoryApp>. This is where the addon will be registered.
Also check for an existing _Imports.razor to understand which namespaces are globally available.
Step 3: Determine which components to create
An addon can consist of up to three Razor components:
| Component type | When to create |
|---|---|
| Toolbar content | When you need a button, toggle, or menu in the top toolbar |
| Panel | When you need a new tab in the bottom panel area |
| Preview decorator | When you need to inject CSS/JS or react to toolbar state inside the preview frame |
Create only the components that are needed. A simple toggle+CSS addon needs just a toolbar component and a decorator.
Step 4: Create the Razor components
Folder convention
Place all files for one addon in a dedicated subfolder inside the stories project:
MyApp.Stories/
└── Addons/
└── MyFeature/
├── MyFeatureToolbarContent.razor
├── MyFeaturePanel.razor (if needed)
└── MyFeaturePreviewDecorator.razor (if needed)
Toolbar content component
Receives mutable GlobalArguments as a cascading parameter. Write to it to propagate state to the preview decorator.
@using BlazingStory.Addons
@using BlazingStory.ToolKit.Buttons
@using BlazingStory.ToolKit.Icons
@inject IJSRuntime JSRuntime
<IconButton Icon="SvgIconType.Grid"
Title="Toggle my feature"
Active="@_enabled"
OnClick="OnClick" />
@code {
[CascadingParameter]
public GlobalArguments Globals { get; set; } = default!;
private bool _enabled = false;
private void OnClick()
{
_enabled = !_enabled;
Globals["myfeature.enabled"] = _enabled ? "true" : null;
}
}
Key rules for toolbar content:
- Cascading parameter type is
GlobalArguments(mutable, fromBlazingStory.Addons). - Use
Globals["key"] = valueto share state with the decorator. Keys are arbitrary strings; use namespaced names (e.g.,"myaddon.key"). - Values stored in
GlobalArgumentsare serialized as strings in the decorator — booleans become"True"/"False"or use"true"/nullexplicitly. - To persist state across page loads, use
@inject IJSRuntime JSRuntimeand call localStorage (e.g., pattern used in built-in addons). - Use
ToolKitcomponents for visual consistency (see ToolKit section below).
Panel component
Receives IStory as a cascading parameter. Use <PanelTitle> for the tab label.
@using BlazingStory.Abstractions
@using BlazingStory.Addons
<PanelTitle>
My Panel
</PanelTitle>
<div class="my-panel">
<!-- panel content here -->
</div>
@code {
[CascadingParameter(Name = "Story")]
public IStory? Story { get; set; }
}
Key rules for panels:
<PanelTitle>content is rendered as the tab label via aSectionContentmechanism — it does not appear inline in the component output.- The
Storycascading parameter gives access to the currently selected story metadata. - Scoped CSS is not supported in addon components. Use a regular
.cssfile and load it with<ImportStyleSheet Href="..." />or a<link>tag.
Preview decorator component
Rendered alongside (not wrapping) the story in the preview frame. Receives read-only cascading parameters.
@using BlazingStory.Abstractions
@code {
[CascadingParameter(Name = "Globals")]
public IReadOnlyDictionary<string, string>? Globals { get; set; }
[CascadingParameter(Name = "Args")]
public IReadOnlyDictionary<string, string>? Args { get; set; }
[CascadingParameter(Name = "Story")]
public IStory? Story { get; set; }
}
To inject a conditional <style> or invoke JS based on toolbar state:
@inject IJSRuntime JSRuntime
@if (_enabled)
{
<style>* { outline: 1px solid red; }</style>
}
@code {
[CascadingParameter(Name = "Globals")]
public IReadOnlyDictionary<string, string>? Globals { get; set; }
private bool _enabled = false;
protected override async Task OnParametersSetAsync()
{
_enabled = Globals?.TryGetValue("myfeature.enabled", out var v) == true && v == "true";
}
}
Key rules for preview decorators:
- Cascading parameter type is
IReadOnlyDictionary<string, string>?(read-only, string values only). - Values from
GlobalArgumentsarrive as strings. Booleans written as"true"/nullcan be checked withv == "true". - The decorator is a sibling to the story component, not a wrapper around it.
- Scoped CSS is not supported — use inline
<style>tags orImportStyleSheet.
IStory cascading parameter reference
Both the panel and the preview decorator receive the currently selected story as a [CascadingParameter(Name = "Story")] IStory? Story. Members on BlazingStory.Abstractions.IStory:
| Member | Type | Description |
|---|---|---|
Title | string | Display title of the story (e.g., "Examples/UI/Button"). |
Name | string | Name of this story (e.g., "Primary"). |
Description | RenderFragment? | Optional descriptive content render fragment. |
ComponentType | Type | CLR type of the target UI component. |
StoriesRazorDescriptor | StoriesRazorDescriptor | Descriptor of the Stories Razor component defining this story. |
Context | IStoryContext | Arguments and parameter state for the story (see below). |
NavigationPath | string | Navigation path string (e.g., "examples-ui-button--primary"). |
IStory.Context exposes IStoryContext with:
| Member | Type | Description |
|---|---|---|
Args | IReadOnlyDictionary<string, object?> | Current argument values keyed by parameter name. |
Parameters | IEnumerable<IComponentParameter> | Component parameters associated with this story. |
ArgumentChanged | event AsyncEventHandler? | Raised when any argument value changes. |
ArgumentsReset | event AsyncEventHandler? | Raised when arguments are reset to defaults. |
ShouldRender | event EventHandler? | Raised to request a re-render of the story. |
GetNoEventParameterCount() | int | Count of parameters that are not event callbacks. |
InitArgument(name, value) | void | Initialize an argument with a name and value. |
ResetArgumentsAsync() | ValueTask | Reset all arguments to their defaults. |
AddOrUpdateArgumentAsync(name, newValue) | ValueTask | Add or update an argument value. |
InvokeShouldRender() | void | Notify the story that it should re-render. |
Each IComponentParameter in Context.Parameters exposes:
| Member | Type | Description |
|---|---|---|
Name | string | Parameter name. |
Type | Type | CLR type of the parameter. |
TypeStructure | TypeStructure | Nullability and generic structure of the parameter type. |
Summary | MarkupString | Summary description from XML documentation. |
Required | bool | Whether the parameter is required. |
Control | ControlType | UI control type used to edit this parameter. |
DefaultValue | object? | Default value of the parameter. |
UpdateSummaryFromXmlDocCommentAsync() | ValueTask | Refresh Summary from the XML doc comment file. |
GetParameterTypeStrings() | IEnumerable<string> | String representations of the parameter type. |
Usage notes:
- Subscribe to
Context.ArgumentChanged/ArgumentsResetinOnParametersSetwhen a panel needs to re-render on argument updates; unsubscribe onIDisposable.Dispose. - Treat
Argsas read-only snapshots; mutate state viaAddOrUpdateArgumentAsyncinstead of writing into the dictionary. Storymay benullbefore a story is selected — always null-check.
Step 5: Create the addon class
Create a C# class implementing IAddon in the same folder:
using BlazingStory.Addons;
namespace MyApp.Stories.Addons.MyFeature;
public class MyFeatureAddon : IAddon
{
public void Initialize(IAddonBuilder builder)
{
builder.AddToolbarContent<MyFeatureToolbarContent>(order: 1000,
match: viewMode => viewMode is ViewMode.Story or ViewMode.Docs);
builder.AddPanel<MyFeaturePanel>(order: 1000,
match: viewMode => viewMode == ViewMode.Story);
builder.AddPreviewDecorator<MyFeaturePreviewDecorator>();
}
}
Key rules for the addon class:
ordercontrols position within each slot. Built-in addons use 100–900. Custom addons are typically placed after the built-ins (on the right), so1000+is the common choice. However, to place a custom addon before the built-ins (on the left), use a value below 100; to place it between specific built-ins, pick a value that fits the desired position within the 100–900 range.matchpredicate controls visibility.ViewModevalues:Story,Docs,CustomPage.AddPreviewDecoratorhas noorderormatch— decorators are always active.- Omit
AddPanel,AddToolbarContent, orAddPreviewDecoratorcalls for component types you are not using.
Step 6: Register the addon
Open the App.razor (or equivalent) file that contains <BlazingStoryApp> and add the registration:
<BlazingStoryApp OnInitialize="builder => builder.Addons.Register<MyFeatureAddon>()" />
If OnInitialize already has content, extract it into a method:
<BlazingStoryApp OnInitialize="Configure" />
@code {
private static void Configure(IBlazingStoryConfigurator builder)
{
builder.Addons.Register<ExistingAddon>();
builder.Addons.Register<MyFeatureAddon>();
}
}
BlazingStory.ToolKit components
Use these components (already available in the stories project) to keep the addon UI consistent with the built-in addons:
| Component | Use for |
|---|---|
<IconButton Icon="SvgIconType.X" Active="..." OnClick="..."> | Toolbar toggle buttons |
<PopupMenu><Trigger>...</Trigger><MenuItems>...</MenuItems></PopupMenu> | Dropdown menus in toolbar |
<MenuItem OnClick="..." Active="..."> | Menu items inside <PopupMenu> |
<MenuItemDivider /> | Dividers between menu item groups |
<Badge Text="..." /> | Count badges in panel titles |
<ImportStyleSheet Href="..." /> | Load/unload a stylesheet conditionally |
<ToolBar>, <TabButton>, <TabButtonGroup> | Tab UIs inside panels |
<Separator />, <Spacer /> | Toolbar spacing |
Namespace reference
Addon components and classes reference types from several BlazingStory packages. Add the matching @using (Razor) or using (C#) directive to each file that references the type. Namespace-per-type cheatsheet:
| Namespace | Types defined there |
|---|---|
BlazingStory.Abstractions | IStory, IStoryContext, IComponentParameter |
BlazingStory.Addons | IAddon, IAddonBuilder, GlobalArguments, PanelTitle, ViewMode |
BlazingStory.ToolKit.Buttons | IconButton, ToggleButton, SquareButton, CornerButton, ResetButton |
BlazingStory.ToolKit.Icons | SvgIconType, SvgIcon, Badge |
BlazingStory.ToolKit.Menus | PopupMenu, MenuItem, MenuItemDivider |
BlazingStory.ToolKit.Styles | ImportStyleSheet |
BlazingStory.ToolKit.ToolBar | ToolBar, TabButton, TabButtonGroup, Separator, Spacer |
BlazingStory.ToolKit.Inputs | ColorInput, NumberInput, TextArea, Select, RadioGroup, NullInputRadio |
Rules of thumb:
- Toolbar content components typically need
@using BlazingStory.Addons, plus ToolKit namespaces for whichever UI components are used (e.g.,Buttons+Iconsfor<IconButton Icon="SvgIconType.X" />). - Panel components typically need
@using BlazingStory.Abstractions(forIStory) and@using BlazingStory.Addons(for<PanelTitle>). - Preview decorator components need
@using BlazingStory.Abstractionsonly when they consume theIStorycascading parameter. - Addon classes (C#) need
using BlazingStory.Addons;(forIAddon,IAddonBuilder,ViewMode).
Step 7: Verify
After creating all files, summarize:
- Files created (component(s), addon class)
- Which slots are registered (toolbar / panel / decorator) and their
order - The
ViewModematch logic applied - The registration line added to
App.razor - Any assumptions made (e.g., localStorage persistence omitted for simplicity)