Interface IBowireRailContribution

Namespace
Kuestenlogik.Bowire.Plugins
Assembly
Kuestenlogik.Bowire.dll

Descriptor contributed by a package that wants to add a rail (a left-strip activity-bar icon + its associated sidebar / main-pane view) to the Bowire workbench.

public interface IBowireRailContribution

Remarks

Rails are the top-level navigation primitives in the workbench shell: Discover, Workspaces, Recordings, Mocks, Flows, Proxy, Benchmarks, Security, &c. Each rail used to be hardcoded into the JS bundle. With #294 every rail goes through this descriptor so embedded hosts can pick the rails they ship — drop the Security NuGet, the Security rail disappears from the strip; drop AI, the Assistant module unwires itself.

The descriptor is intentionally narrow. It carries enough metadata for the JS renderer (id, label, icon, sort, group, default-enabled) to draw the rail icon + register the route, but the actual sidebar / main-pane DOM stays inside the existing per-feature JS module the rail bundles with — the descriptor only opts the rail into the strip + Settings → Rail modes.

Implementations are auto-discovered by Discover(ILogger?) when the host invokes AddBowire(IServiceCollection); hosts can also register a descriptor explicitly via services.AddBowireRail<TRail>().

Properties

AlwaysOn

Whether the rail is part of the locked always-on set. Differs from DefaultEnabled: an always-on rail also can't be disabled at all. Defaults to false; only the four core rails (Home, Discover, Compose, Workspaces) ship as always-on.

bool AlwaysOn { get; }

Property Value

bool

DefaultEnabled

Whether the rail is part of the always-on minimum set. Always-on rails cannot be disabled via Settings → Rail modes (the checkbox renders greyed with a "Built-in" badge). Set false for every rail the operator might reasonably want to switch off.

bool DefaultEnabled { get; }

Property Value

bool

DisplayName

Human-readable label shown in the rail tooltip + Settings.

string DisplayName { get; }

Property Value

string

Group

Visual group the rail belongs to. Adjacent rails with different group values get a divider between them. Built-in groups: "work", "scenarios", "quality", "hardening". New groups land at the bottom of the rail by default.

string Group { get; }

Property Value

string

HideFromRail

When true, the rail is in the catalogue (so routing still works when another rail's tree dispatches into it — e.g. Collections / Environments dispatched from a workspace) but no dedicated rail-strip icon renders. Defaults to false.

bool HideFromRail { get; }

Property Value

bool

IconKey

Icon key from the workbench's SVG-icon catalogue (e.g. "house", "discover", "shield"). Resolved JS-side via svgIcon(key). Unknown keys fall back to a generic square placeholder.

string IconKey { get; }

Property Value

string

Id

Stable identifier (e.g. "discover", "recordings", "security"). Must match the rail-mode id the JS bundle uses for routing — operators' localStorage.bowire_rail_mode values + deep links key off this. Case-sensitive (snake-lower).

string Id { get; }

Property Value

string

MainPaneRendererKey

Identifier of a JS-side function the rail package's JS fragment registers on window.__bowireRailRenderers at load time. The core renderMain dispatcher looks up the renderer by id and invokes it instead of hard-coding the per-rail branch. Empty / null means "no rail-owned renderer; fall back to the core dispatcher arm".

string? MainPaneRendererKey { get; }

Property Value

string

Remarks

Convention: railId + 'Main' (e.g. "recordingsMain", "benchmarksMain"). The hosted JS fragment writes window.__bowireRailRenderers["recordingsMain"] = function () { ... }; inside the shared IIFE. The renderer takes no arguments and returns the DOM root for the main pane.

RequiresWorkspace

When true, the rail's view only makes sense inside an active workspace (Recordings / Mocks / Collections / Flows / Benchmarks / Compose — anything that persists artefacts to a workspace folder). The rail button stays clickable (so tour spotlighting + the rail strip's tab semantics keep working) but a click without an active workspace redirects the operator to the Home rail, where the "Create your first workspace" CTA lives, and fires an explanatory toast. Defaults to false — rails that work standalone (Home, Discover, Traffic, Workspaces, Security, Settings) stay reachable at all times.

bool RequiresWorkspace { get; }

Property Value

bool

SidebarKind

Sidebar template the rail renders. Recognised values: "none", "services", "collections", "environments", "recordings", "mocks", "workspaces", "sources", "benchmarks", "flows", "proxy", "security", "library". Adding a new value requires a matching arm in render-sidebar.js's dispatcher — see the sidebar.kind comment block in that file.

string SidebarKind { get; }

Property Value

string

SidebarRendererKey

Identifier of a JS-side function the rail package's JS fragment registers on window.__bowireRailRenderers at load time. The core renderSidebar dispatcher looks up the renderer by id and invokes it instead of hard-coding the per-rail branch. Empty / null means "no rail-owned renderer; fall back to the core dispatcher arm" — so the slice can be moved incrementally rail-by-rail without breaking the bundle.

string? SidebarRendererKey { get; }

Property Value

string

Remarks

Convention: railId + 'Sidebar' (e.g. "recordingsSidebar", "proxySidebar"). The hosted JS fragment writes window.__bowireRailRenderers["recordingsSidebar"] = function () { ... }; inside the shared IIFE. The renderer takes no arguments and returns the DOM root to mount as the sidebar — same contract as the legacy renderRecordingsSidebar() &c. arms.

SortIndex

Sort priority. Lower values appear higher in the rail strip. The built-in catalogue uses 100-step intervals so third-party rails can wedge between two built-ins without re-numbering everything.

int SortIndex { get; }

Property Value

int