Navigator
The identity and state of the user agent: device capabilities, languages, online status, native sharing, app badges, beacons and vibration - queried and driven from C#.
@inject Bit.Butil.Navigator navigatorMDN reference
Coarse hardware hints for adaptive experiences: approximate RAM in gigabytes, the number of logical CPU cores, and how many simultaneous touch points the device supports.
float memory = await navigator.GetDeviceMemory(); // e.g. 8
float cores = await navigator.GetHardwareConcurrency(); // e.g. 12
byte touchPoints = await navigator.GetMaxTouchPoints(); // 0 on most desktopsLanguage and browser traits
GetLanguage / GetLanguages / GetUserAgent / IsPdfViewerEnabled / IsWebDriverRead the user's preferred languages in priority order, the raw user-agent string, whether the browser renders PDFs inline, and whether the session is driven by automation such as Selenium or Playwright.
string language = await navigator.GetLanguage(); // "en-US"
string[] languages = await navigator.GetLanguages(); // ["en-US", "en", ...]
string userAgent = await navigator.GetUserAgent();
bool pdfInline = await navigator.IsPdfViewerEnabled();
bool automated = await navigator.IsWebDriver();Reports whether the browser believes it has network connectivity. A false value is trustworthy (definitely offline); true only means some network interface is up.
bool online = await navigator.IsOnLine();Toggle DevTools network throttling to Offline and check again.
Asks whether the browser has user input waiting that this task is holding up - the check that turns a long loop into one that yields only when yielding would help. The demo runs a 600 ms busy loop that polls between chunks and yields whenever input is pending, then carries on; click somewhere while it runs. Experimental and Chromium-only: elsewhere it answers false, so treat it as a hint and always bound the loop with a deadline of your own.
// IsInputPending only ever lets you yield *sooner*; a deadline is what
// guarantees you yield at all on a browser that always answers false
var nextYield = DateTime.UtcNow.AddMilliseconds(50);
while (thereIsMoreWork)
{
DoAChunkOfWork();
if (await navigator.IsInputPending() || DateTime.UtcNow >= nextYield)
{
await Task.Yield(); // let the browser answer the user first
nextYield = DateTime.UtcNow.AddMilliseconds(50);
}
}
// includeContinuous also counts mousemove, pointermove, wheel and drag -
// which fire constantly, so leave it false unless the work is truly interruptible
bool anyPointerMovement = await navigator.IsInputPending(includeContinuous: true);Shows a count (or a plain dot when no number is given) on the app icon of an installed PWA - the same affordance native apps use for unread counts.
await navigator.SetAppBadge(7); // show "7" on the app icon
await navigator.SetAppBadge(); // show a generic dot
await navigator.ClearAppBadge(); // remove the badgeSendBeacon queues a small fire-and-forget HTTP POST that survives page unload - the reliable way to flush analytics. Vibrate plays a pattern of vibration/pause durations in milliseconds on supporting hardware.
bool queued = await navigator.SendBeacon("/api/analytics", "{\"event\":\"page-close\"}");
bool accepted = await navigator.Vibrate([200, 100, 200]);Vibration only has a physical effect on devices with a vibration motor.
Share, ShareFiles and the app badge APIs require a secure context (https),
and sharing must be triggered by a user gesture such as a button click. The share sheet is mostly a
mobile affordance - many desktop browsers reject it - and badges only appear on installed PWAs.
API reference
Task<float> GetDeviceMemory()Task<float> GetHardwareConcurrency()Task<string> GetLanguage()Task<string[]> GetLanguages()Task<byte> GetMaxTouchPoints()Task<bool> IsOnLine()Task<bool> IsPdfViewerEnabled()Task<string> GetUserAgent()Task<bool> IsWebDriver()Task<bool> CanShare()Task Share(ShareData data)Task<bool> ShareFiles(string? title, ShareFile[] files, string? text = null, string? url = null)Task SetAppBadge(int? contents = null)Task ClearAppBadge()Task<bool> SendBeacon(string url, object? data = null)Task<bool> Vibrate(int[] pattern)Task<bool> IsCookieEnabled()Task<string?> GetDoNotTrack()Task<UserActivationState?> GetUserActivation()Task<bool> IsInputPending(bool includeContinuous = false)class UserActivationState { bool HasBeenActive; bool IsActive; }Task<bool> CanRegisterProtocolHandler()Task<bool> RegisterProtocolHandler(string scheme, string url)Task<bool> UnregisterProtocolHandler(string scheme, string url)Task<bool> CanGetInstalledRelatedApps()Task<RelatedApp[]> GetInstalledRelatedApps()class RelatedApp { string Id; string Platform; string Url; string Version; }