npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wincc-oa/webui-runtime

v1.2.2

Published

WinCC Open Architecture Dashboard project.

Readme

ETM Professional Control – WebUI runtime

This workspace contains the Dashboard web application released in WinCC OA 3.21 by ETM Professional Control. This application can be extended with Web Components. This file explains how to develop and build the web app locally and how to run it together with your WinCC OA project.

More information about Dashboard basics at run-time can be found in the official documentation: https://www.winccoa.com/documentation/WinCCOA/latest/en_US/Dashboard/topics/Dashboard_Basics.html

Workspace Overview

This workspace is used to build and modify the web application. The individual building blocks are split into separate npm packages (widgets, API, and service implementations). In this workspace these packages are already installed or can be installed via npm.

WinCC OA uses individual projects that must be configured accordingly. Within these projects, the WebServer and WebSocket server run and interact with this web app.

Setup & Operation

Initial Setup (First-Time Only)

The @wincc-oa/webui-runtime npm package ships the workspace sources. To start a new project, install the package and run the webui-runtime-init command to copy its contents into your project directory:

mkdir my-dashboard && cd my-dashboard
npm install @wincc-oa/webui-runtime
npx webui-runtime-init

The init command copies the workspace files (apps, libs, configs, scripts) into the current directory. It is interactive by default — pass -y for non-interactive use (e.g. in CI):

npx webui-runtime-init -y

After the files are deployed, install the development dependencies and create the oa-data/ directory:

npm install --no-audit --no-fund --include dev
npm run init:oa-data

The workspace is now ready. Continue with Standard Setup for the day-to-day development workflow.

Standard Setup

  1. Install dependencies (skip if you just ran the Initial Setup):
npm install
  1. Start development (connected to a WinCC OA project):

Assuming WinCC OA is configured so the WebServer is available at https://localhost and the project is located at C:\WinCC_OA_Proj\3.21\example_dashboard, start the web app to work against the configured server:

# Linux / Mac / Git Bash
BASE_URL="https://localhost" npm run start

# Windows PowerShell
$env:BASE_URL="https://localhost"; npm run start

# Windows CMD
set BASE_URL=https://localhost&& npm run start

The served application is then available at http://localhost:4300

  1. Build for production and deploy into the WinCC OA project:

Using environment variable to set output directory:

# Linux / Mac / Git Bash
OUT_DIR="/path/to/WinCC_OA_Proj/3.21/example_dashboard/data/dashboard-wc" npm run build

# Windows PowerShell
$env:OUT_DIR="C:\WinCC_OA_Proj\3.21\example_dashboard\data\dashboard-wc"; npm run build

# Windows CMD
set OUT_DIR=C:\WinCC_OA_Proj\3.21\example_dashboard\data\dashboard-wc&& npm run build

Important: In Windows CMD, do not add a space before && - it becomes part of the variable value and the path will not resolve correctly.

Default behavior (no OUT_DIR): Builds to dist/ folder within the webui-runtime workspace.

Available build scripts:

  • npm run build - Full build: shared bundles + main app + standalone pages
  • npm run build:shared-bundles - Build shared bundles only (lit, ix, rxjs, wui)
  • npm run build:pages - Build standalone pages only (for fast iteration during page development)
  • npm run init:oa-data - Create the oa-data/ directory for translations, icons, and SVGs
  • npm run deploy:oa-data - Deploy oa-data/ contents to the WinCC OA project (also runs as part of build)

Notes:

  • BASE_URL: Base URL of the WinCC OA WebServer used by the web app to access backend APIs/assets.
  • OUT_DIR: Target directory for all build commands (environment variable). Works with build, build:shared-bundles, build:app, and build:pages.
  • Full build order: shared bundles first, then main app and finally standalone pages.

Build Architecture

The build process uses import maps to share dependencies across the main app and standalone pages.

Build Phases

  1. Shared bundles build (build:shared-bundles) - Creates shared dependency bundles in entry/
  2. App build (build:app) - Creates main application with shared bundles externalized
  3. Pages build (build:pages) - Creates standalone pages (can be run independently for fast iteration)

Import Map

The index.html contains an import map that redirects bare module specifiers to pre-built shared bundles:

| Specifier Pattern | Bundle | | ------------------------------------ | --------------------- | | @siemens/ix, @siemens/ix-echarts | ./entry/ix.js | | @siemens/ix-icons | ./entry/ix-icons.js | | lit, @lit/*, lit-* | ./entry/lit.js | | rxjs | ./entry/rxjs.js | | @wincc-oa/wui-*, etc. | ./entry/wui.js |

This allows standalone pages to use the same dependencies as the main app without bundling them separately.

Shared Bundle Configuration

The bundle exports are defined in config/shared-bundle-exports.jsonc. This file is not copied to production - it's build-time only configuration.

For details on how bundles work, available entry types (manual, autoDiscover, importMapEntries, etc.), and how to add custom context/widget components via aliasing, see docs/knowledge/webui-runtime-shared-bundles.md.

To regenerate the export entry files after modifying the config:

npm run generate:shared-bundles

Standalone Pages

Standalone pages are full-screen views that run alongside the dashboard. They can connect to WinCC OA datapoints for real-time data and are added to the application via menu configuration.

Adding a new standalone page:

  1. Create a new .ts file in libs/default-components/src/lib/standalone-pages/
  2. Add a menu entry in apps/dashboard-wc/config/menuconfig.jsonc
  3. Run npm run build:pages for fast iteration, or npm run build for a full rebuild

Pages are automatically discovered at build time - no build configuration changes needed.

Minimal Example

import { OaRxJsApi } from '@etm-professional-control/oa-rx-js-api';
import { LitElement, css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { Subscription, map } from 'rxjs';
import { container } from 'tsyringe';

@customElement('wui-my-page')
export class WuiMyPage extends LitElement {
  static override styles = css`
    :host {
      display: block;
      padding: 1rem;
    }
  `;

  @state() private myValue = 0;
  private readonly oaRxJsApi = container.resolve<OaRxJsApi>(OaRxJsApi);
  private subscriptions: Subscription[] = [];

  override connectedCallback(): void {
    super.connectedCallback();
    const sub = this.oaRxJsApi
      .dpConnect('System1:ExampleDP_Arg1.', true)
      .pipe(map((data) => (data.value[0] as number) ?? 0))
      .subscribe((value) => {
        this.myValue = value;
      });
    this.subscriptions.push(sub);
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();
    for (const sub of this.subscriptions) sub.unsubscribe();
    this.subscriptions = [];
  }

  override render() {
    return html`<h2>Value: ${this.myValue}</h2>`;
  }
}

Menu Configuration

Add an entry to apps/dashboard-wc/config/menuconfig.jsonc:

{
  "title": { "en_US": "My Page", "de_DE": "Meine Seite" },
  "icon": "capacity",
  "path": "/my-page",
  "module": "/data/dashboard-wc/pages/my-page.js",
  "routeId": "my-page",
  "component": "wui-my-page",
  "permission": ["connected"]
}

For declarative data binding, you can also use <wui-context-generator> with context types (data-point, translate, group, etc.) instead of direct API subscriptions.

Detailed guides for standalone page development - including advanced data binding, context types, styling, and SVG graphics - are available in the online documentation.


Dynamic Widget Loading

Widgets can access services from the host application via the tsyringe DI container.

Available Services

| Token | Class | Description | | --------------------------- | ------------------------- | ---------------------------------------------------- | | OaRxJsApi | OaRxJsApi | WinCC OA backend API (dpGet, dpSet, dpConnect, etc.) | | WuiBackendConnectionToken | WuiBackendConnection | WebSocket connection state monitoring | | WuiSettingsService | WuiSettingsService | Backend settings (OIDC, WebSocket servers, etc.) | | WuiUserService | WuiUserService | Current user information and permissions | | DateService | DateService | Date/time utilities with backend time sync | | WuiRouterServiceToken | WuiRouterFacade | Router navigation and route management | | WuiToastService | WuiToastService | Toast notifications (success, error, warning) | | WuiCleanupService | WuiCleanupService | Resource cleanup on logout/session end | | WuiConfigServiceToken | WebuiConfigService | App and menu configuration from JSON files | | RuntimeComponentService | RuntimeComponentService | Runtime component installation and management |

Service Usage Examples

import { OaRxJsApi } from '@etm-professional-control/oa-rx-js-api';
import { container } from 'tsyringe';

const api = container.resolve(OaRxJsApi);

// Read a single datapoint
api.dpGet(['System1:ExampleDP_Arg1.']).subscribe((data) => {
  console.log('Value:', data.value);
});

// Subscribe to datapoint changes
const subscription = api.dpConnect('System1:ExampleDP_Arg1.').subscribe((data) => {
  console.log('New value:', data.value);
});

// Write to a datapoint
api.dpSet('System1:ExampleDP_Arg1.', 42).subscribe((result) => {
  console.log('Write successful:', result);
});

// Execute custom command
api.customCommand('myFunction', { param1: 'value' }).subscribe((result) => {
  console.log('Result:', result);
});
import { WuiBackendConnection } from '@wincc-oa/wui-oarxjs-data';
import { WuiBackendConnectionToken } from '@wincc-oa/wui-shared/tokens/wui-backend-connection.token.js';
import { container } from 'tsyringe';

container.registerSingleton(WuiBackendConnectionToken, WuiBackendConnection);
const connection = container.resolve(WuiBackendConnectionToken);

// Monitor connection state
connection.status$.subscribe((state) => {
  // state: 'connected' | 'disconnected' | 'connecting' | 'reconnecting' | 'error'
  console.log('Connection state:', state);
});

// Check if currently connected
if (connection.status$.value === 'connected') {
  // Safe to perform operations
}
import { WuiUserService } from '@wincc-oa/wui-iam-data';
import { container } from 'tsyringe';

const userService = container.resolve(WuiUserService);

// Access user properties
console.log('User ID:', userService.id);
console.log('Username:', userService.name);
console.log('Locale:', userService.locale);

// Check permissions
if (userService.canWrite) {
  // Show edit UI
}
if (userService.canEdit) {
  // Enable edit mode
}
if (userService.canPublish) {
  // Show publish button
}

// Access favorites
const favorites = userService.favorites; // number[]

// Wait for user data to load
userService.ready$.subscribe((ready) => {
  if (ready) {
    console.log('User data loaded');
  }
});
import { WuiToastService } from '@wincc-oa/wui-shared/services/wui-toast/wui-toast.service.js';
import { container } from 'tsyringe';

const toastService = container.resolve(WuiToastService);

// Success notification
await toastService.success('Operation completed successfully');

// Error notification
await toastService.error('Failed to save data', 'Error Title');

// Warning notification
await toastService.warning('Please check your input');

// Custom toast with full options
await toastService.toast({
  message: 'Custom message',
  type: 'info',
  autoClose: true,
  position: 'top-right'
});
import { WuiRouterFacade } from '@wincc-oa/wui-models/interfaces/wui-router/wui-router.facade.js';
import { WuiRouterServiceToken } from '@wincc-oa/wui-shared/tokens/wui-router-service.token.js';
import { container } from 'tsyringe';

const router = container.resolve<WuiRouterFacade>(WuiRouterServiceToken);

// Navigate to a route
router.render('/dashboard/overview');

// Navigate with parameters
const pathname = router.getPathname('dashboard', { id: '123' });
router.render(pathname, true);

// Get current route information
const routeId = router.getRouteId();
const params = router.getParam('id');
const searchParams = router.searchParams;
import { DateService } from '@wincc-oa/wui-shared/services/date/date.service.js';
import { container } from 'tsyringe';

const dateService = container.resolve(DateService);

// Get backend UTC time
const backendTime = await dateService.getBackendUTCTime();
console.log('Backend time:', backendTime);

// Get time offset between frontend and backend
const offset = dateService.getTimeOffsetToBackend();
console.log('Time offset (ms):', offset);

// Check if offset exceeds threshold (30 seconds)
const exceedsThreshold = await dateService.doesOffsetExceedThreshold();
if (exceedsThreshold) {
  console.warn('Time offset too large!');
}

// Monitor offset continuously
dateService.doesOffsetExceedThreshold$.subscribe((exceeds) => {
  if (exceeds) {
    // Show warning to user
  }
});

Utility Functions

// For reactive Lit templates (auto-updates on language change)
import { translate } from 'lit-translate';
import { html } from 'lit';

html`<h1>${translate('WUI_General.App.Title')}</h1>`;

// For static usage (JavaScript/TypeScript)
import { get } from 'lit-translate';

const title = get('WUI_General.App.Title');
console.log(title); // => 'WinCC OA Dashboard'

// Multi-language strings (objects with locale keys)
import { translateOrLocalize, translateOrLocalizeStatic } from '@wincc-oa/wui-i18n-shared/localize-multilang.js';

// Works with translation keys
html`<h1>${translateOrLocalize('WUI_General.App.Title')}</h1>`;

// Works with multi-language objects
html`<h1>${translateOrLocalize({ en_US: 'Title', de_DE: 'Titel' })}</h1>`;

// Static version (for JavaScript, not Lit templates)
const text = translateOrLocalizeStatic('WUI_General.App.Title');
import { getCssCustomPropertyValue } from '@wincc-oa/wui-shared/helper-css.js';

// Transform OA color names to HTML colors via CSS variables
const color1 = getCssCustomPropertyValue('--oa-color--FwGreen');
// => '#00ff00' (resolved from CSS variable)

const color2 = getCssCustomPropertyValue('var(--oa-color--FwRed)');
// => '#ff0000' (works with var() syntax too)

// Regular colors pass through unchanged
const color3 = getCssCustomPropertyValue('#123456');
// => '#123456'

// Use in Lit component styles
import { css, html, LitElement } from 'lit';

class MyComponent extends LitElement {
  static styles = css`
    :host {
      /* OA colors are available as CSS variables */
      color: var(--oa-color--FwGreen);
      background: var(--oa-color--FwWhite);
    }
  `;
}
import { localizeDate } from '@wincc-oa/wui-i18n-shared/localize-date.js';

// Format dates according to current locale
const date = new Date();
const formattedDate = localizeDate(date); // e.g., "12/31/2023" or "31.12.2023"

Basic Example

import { LitElement, html } from 'lit';
import { container } from 'tsyringe';
import { OaRxJsApi } from '@etm-professional-control/oa-rx-js-api';

export class MyWidget extends LitElement {
  constructor() {
    super();
    this.api = container.resolve(OaRxJsApi);
  }

  connectedCallback() {
    super.connectedCallback();
    // Subscribe to a datapoint
    this.subscription = this.api.dpConnect('System1:ExampleDP_Arg1.').subscribe((data) => console.log('Value:', data.value));
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this.subscription?.unsubscribe();
  }

  render() {
    return html`<p>My Widget</p>`;
  }
}

customElements.define('my-widget', MyWidget);

See widgets-v2/wc/dynamic-widget.js for a complete example.

Routing

The application uses Vaadin Router with hash-based routing and config-driven routes from menuconfig.json. Routes are generated at startup from the menu configuration and registered through the DI container; workspace-supplied route generators contribute named subtrees. Authentication routes (/login, /logout) and the error/404 routes are part of the runtime's static frame and cannot be overridden by menu configuration.

i18n

For translations we use the lit-translate library.

Additionally, the @wincc-oa/wui-i18n package provides custom functions and directives to handle localization for dates, numbers, etc.

Documentation

Technical documentation is included in the docs/knowledge/ directory:

  • Widget development: Creating widgets, JSON schemas, integrating external components
  • Dashboard customization: Menu configuration, branding, custom themes
  • Standalone pages: Building full-screen pages with data binding
  • Architecture: Application shell, routing, DI setup, controllers
  • Siemens iX: Component usage, icons, color system
  • Web technologies: Lit state management, WebComponent best practices, styling
  • Backend integration: WebSocket protocol, authentication, API reference