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

@fastann/angular

v1.0.6

Published

Annotation library — Angular 17+ adapter (ng-packagr)

Readme

@fastann/angular

Angular 17+ adapter for FastAnn — a lightweight annotation overlay library that lets you attach contextual notes, rules, warnings, tasks, and user comments to any element in your UI.

  • Zero layout impact — annotations render as floating dialogs, not inline elements
  • Signals-based reactivity (Angular 17+ Signals API)
  • Optional REST backend for persisting annotations across users
  • Supports system annotations (defined in code), database annotations (loaded from API), and user comments
  • Dark mode, multi-language (en, vi, zh, ja, ko, fr, de, es, pt, it, ru, tr, id, th, hi, ar, nl, pl), permission system

Table of Contents

  1. Requirements
  2. Installation
  3. Peer Dependencies
  4. Global Styles
  5. Step 1 — Provide the API URL
  6. Step 2 — Add the Stage Component
  7. Step 3 — Make Cells Annotatable
  8. Step 4 — Mark Any Element by Key
  9. Step 5 — (Optional) Add the Built-in Panel
  10. Step 6 — Set the Current User
  11. Step 7 — Multi-language Support
  12. Step 8 — Load Annotations from the API
  13. Vite Pre-bundling Note
  14. API Reference
  15. Annotation Types
  16. Permission Keys

Requirements

| Dependency | Version | |---|---| | Angular | >=17.0.0 | | @angular/common | >=17.0.0 | | @angular/forms | >=17.0.0 | | @angular/router | >=17.0.0 | | rxjs | >=7.0.0 | | Node.js | >=18 |


Installation

npm install @fastann/angular

Peer Dependencies

The library does not bundle Angular itself. Make sure your project already has:

npm install @angular/common @angular/core @angular/forms @angular/router rxjs

Global Styles

Import the required CSS once in your global stylesheet (styles.scss / styles.css):

// styles.scss
@import '@fastann/angular/styles/annotation.css';

Or in angular.json:

"styles": [
  "src/styles.scss",
  "node_modules/@fastann/angular/styles/annotation.css"
]

This file provides dot-badge colors, dialog base styles, and dark-mode variables. Skipping it will result in unstyled badge indicators.


Step 1 — Provide the API URL

In app.config.ts, register the API base URL using the ANN_API_URL injection token. The library uses this to GET and POST annotations from your backend.

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideHttpClient } from '@angular/common/http';
import { ANN_API_URL } from '@fastann/angular';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideAnimations(),
    provideHttpClient(),          // required — services use HttpClient
    { provide: ANN_API_URL, useValue: 'https://your-api.example.com/api' },
  ],
};

No backend? You can omit { provide: ANN_API_URL, ... } — the library falls back to '/api' but all HTTP calls will fail silently. System annotations (defined in code) still work fully offline.


Step 2 — Add the Stage Component

AnnotationStageComponent is the global overlay that renders floating dialogs and SVG connectors. Add it once at the application root so it sits above all page content.

// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { AnnotationStageComponent } from '@fastann/angular';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    RouterOutlet,
    AnnotationStageComponent,   // add here
  ],
  template: `
    <main>
      <router-outlet />
    </main>

    <!-- Place at the end of the template, outside <main> -->
    <app-annotation-stage />
  `,
})
export class AppComponent {}

Step 3 — Make Cells Annotatable

Use the AnnotationCellDirective ([annCell] / ann-cell-key) to attach annotation metadata to any HTML element. The directive:

  • Renders a colored dot-badge inside the element when an annotation exists
  • Handles click-to-open dialog
  • Syncs with AnnotationService and CustomAnnotationService

Import the directive in the component that owns the template:

imports: [AnnotationCellDirective, DotVisiblePipe]

Option A: Inline attribute API (HTML-only)

Declare a system annotation entirely in the template. The library auto-generates an id and persists it to the API if it does not exist yet.

<!-- Table header cell -->
<th
  ann-cell-key="col:sales"
  ann-type="info"
  ann-source="Finance"
  ann-title="Sales (USD)"
  ann-body="Gross sales in USD, net of refunds and discounts."
  ann-tag="KPI"
  [ann-default-size-w]="300"
  [ann-default-size-h]="160"
>
  Sales (USD)
</th>

All ann-* attributes:

| Attribute | Type | Description | |---|---|---| | ann-cell-key | string (required) | Unique key for this cell. Use a stable, path-style string: "col:revenue", "row1:margin" | | [annCell] | Annotation \| undefined | Pass an existing Annotation object (from customService.forCell(key)) instead of declaring inline | | ann-type | AnnotationType | info | rule | comment | warning | formula | action | task | | ann-source | string | Source label shown in the dialog header (e.g. "Finance", "System") | | ann-title | string | Short title | | ann-body | string | Full description body | | ann-tag | string | Tag/label (e.g. "KPI", "Q1 2025") | | ann-status | TaskStatus | to_do | in_progress | done — only relevant for task type | | [ann-default-size-w] | number | Default dialog width in px | | [ann-default-size-h] | number | Default dialog height in px | | [ann-default-pos-x] | number | Default dialog X offset from anchor | | [ann-default-pos-y] | number | Default dialog Y offset from anchor | | [ann-is-syn-db] | boolean | true (default) — sync this annotation to the database on first load |

Option B: Register from component (TypeScript)

For i18n labels or complex logic, register annotations in TypeScript using CustomAnnotationService.registerSystemAnnotation():

// my-page.component.ts
import { Component, OnInit, effect, Injector } from '@angular/core';
import { AnnotationCellDirective, CustomAnnotationService, AnnotationLabelsService } from '@fastann/angular';

@Component({
  selector: 'app-my-page',
  standalone: true,
  imports: [AnnotationCellDirective],
  template: `
    <div ann-cell-key="kpi-revenue">$4.2M</div>
  `,
})
export class MyPageComponent implements OnInit {
  constructor(
    public customService: CustomAnnotationService,
    private labels: AnnotationLabelsService,
    private injector: Injector,
  ) {
    // Re-register when language changes
    effect(() => {
      this.labels.labels(); // reactive dependency
      this.registerAnnotations();
    }, { injector: this.injector, allowSignalWrites: true });
  }

  ngOnInit() {
    this.registerAnnotations();
  }

  private registerAnnotations() {
    const lang = this.labels.getLang();

    this.customService.registerSystemAnnotation('kpi-revenue', {
      type: 'info',
      source: 'Finance',
      title: lang === 'vi' ? 'Tổng doanh thu' : 'Total Revenue',
      body:  lang === 'vi'
        ? 'Doanh thu gộp, đã trừ hoàn trả, quy đổi về USD.'
        : 'Gross revenue net of refunds, FX-normalised to USD.',
      tag: lang === 'vi' ? 'Định nghĩa' : 'Definition',
    });
  }
}

Option C: Dynamic rows from API data

For table rows where the key is built from row data:

<!-- Template -->
<tr *ngFor="let row of rows">
  <td>{{ row.product }}</td>

  <!-- Pass the API annotation object + a unique key per row -->
  <td
    [annCell]="customService.forCell(row.id + ':sales')"
    [ann-cell-key]="row.id + ':sales'"
  >
    {{ row.sales | number }}
  </td>
</tr>

customService.forCell(key) returns the Annotation loaded from the API for that key, or undefined if none exists.


Step 4 — Mark Any Element by Key

AnnotationKeyDirective ([annKey]) marks any element as an annotation anchor without a pre-declared annotation. Use it on headers, labels, or any structural element where users can add their own notes at runtime.

imports: [AnnotationKeyDirective]
<th [annKey]="'th:product'">Product</th>
<label [annKey]="'form:username'">Username</label>

The directive sets data-ck on the element and renders a badge if a custom annotation exists for that key in the database.


Step 5 — (Optional) Add the Built-in Panel

AnnPanelComponent provides a floating panel with filter controls, dark-mode toggle, language switch, and permission management. It is optional — you can build your own toolbar using AnnotationService directly.

// my-page.component.ts
imports: [AnnPanelComponent]
<!-- my-page.component.html -->
<ann-panel />

<div class="page-content">
  <!-- your page -->
</div>

The panel is draggable and stays fixed to the viewport. Place it once per page (or at the app root).


Step 6 — Set the Current User

User identity controls who can perform permission-gated actions (edit, delete, manage permissions). Set the current user after login:

// auth.service.ts or login.component.ts
import { AnnotationAuthService, AnnUser } from '@fastann/angular';

@Injectable({ providedIn: 'root' })
export class AuthService {
  constructor(private annAuth: AnnotationAuthService) {}

  onLoginSuccess(apiUser: any) {
    const annUser: AnnUser = {
      code: apiUser.employee_code,        // e.g. "EMP001"
      name: apiUser.display_name,         // e.g. "Alice"
      role: apiUser.role,                 // e.g. "admin"
      permissions: apiUser.ann_permissions, // AnnPermissionItem[] from API
    };
    this.annAuth.setCurrentUser(annUser);
  }

  onLogout() {
    this.annAuth.setCurrentUser(null);
  }
}

AnnUser is persisted to localStorage automatically and restored on next page load.


Step 7 — Multi-language Support

The library ships with these language codes: en, vi, zh, ja, ko, fr, de, es, pt, it, ru, tr, id, th, hi, ar, nl, pl.

Switch language at runtime:

import { AnnotationLabelsService } from '@fastann/angular';

@Component({ ... })
export class AppComponent {
  constructor(private labels: AnnotationLabelsService) {}

  switchToVietnamese() {
    this.labels.setLang('vi');
  }

  switchToEnglish() {
    this.labels.setLang('en');
  }

  switchToChinese() {
    this.labels.setLang('zh');

    this.labels.setLang('ja');

    this.labels.setLang('fr');
  }
}

The current language is a Signal — all library UI components react automatically.


Step 8 — Load Annotations from the API

Call CustomAnnotationService.loadFromApi() in the component that owns the page, passing the current domain and route. This fetches all annotations for that page from the backend.

// my-page.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Router } from '@angular/router';
import { CustomAnnotationService } from '@fastann/angular';

@Component({ ... })
export class MyPageComponent implements OnInit {
  constructor(
    public customService: CustomAnnotationService,
    private router: Router,
    @Inject(DOCUMENT) private document: Document,
  ) {}

  ngOnInit() {
    const domain = this.document.location.hostname;  // e.g. "app.example.com"
    const route  = this.router.url;                   // e.g. "/sales/dashboard"
    this.customService.loadFromApi(domain, route);
  }
}

The library calls GET /api/Ann/get_all?domain=...&router=...&ann_lang=... and merges results with any system annotations registered in code.


Vite Pre-bundling Note

If you use @angular-devkit/build-angular v17 (which uses Vite as dev server), exclude @fastann/angular from Vite's dep pre-bundling to avoid esbuild conflicts. Add to angular.json:

// angular.json — inside "serve" > "options"
"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "prebundle": {
      "exclude": ["@fastann/angular"]
    }
  }
}

API Reference

Services

AnnotationService

Central store for open dialogs and global UI state.

| Member | Type | Description | |---|---|---| | dialogs | Signal<OpenDialog[]> | Currently open annotation dialogs | | mode | Signal<FilterMode> | Active filter: 'all' | annotation type | 'none' | | dark_mode | Signal<boolean> | Dark mode state | | add_mode | Signal<boolean> | Whether "add annotation" mode is active | | source_filter | Signal<SourceFilter> | 'all' | 'system' | 'user' | | status_filter | Signal<StatusFilter> | 'all' | 'to_do' | 'in_progress' | 'done' | | toggleDark() | void | Toggle dark mode | | toggleAddMode() | void | Toggle add-annotation mode | | setSourceFilter(f) | void | Set source filter | | setStatusFilter(f) | void | Set status filter |

CustomAnnotationService

Manages system + database annotations.

| Member | Description | |---|---| | loadFromApi(domain, router) | Fetch all annotations for the given page from the backend | | registerSystemAnnotation(cellKey, ann) | Declare a system annotation in code | | forCell(cellKey) | Returns the active Annotation for a cell key, or undefined | | entries | Signal<CustomEntry[]> — all loaded annotations |

UserAnnotationService

Manages per-annotation user comments/notes.

| Member | Description | |---|---| | items | Signal<UserAnnotation[]> — all loaded user notes | | loadForAnnotation(id) | Fetch notes for a specific annotation | | hasForCell(annId) | Returns true if there are user notes for this annotation |

AnnotationAuthService

User identity store.

| Member | Description | |---|---| | currentUser | Returns the current AnnUser or null | | setCurrentUser(user) | Set/clear the current user (persisted to localStorage) |

AnnotationLabelsService

Language / i18n service.

| Member | Description | |---|---| | labels() | Signal returning the full AnnotationLabels object | | getLang() | Returns the current Lang code | | setLang(lang) | Switch language: 'en' | 'vi' | 'zh' | 'ja' | 'ko' | 'fr' | 'de' | 'es' | 'pt' | 'it' | 'ru' | 'tr' | 'id' | 'th' | 'hi' | 'ar' | 'nl' | 'pl' |

AnnotationStageService

Internal stage/cell registration. Rarely needed directly.


Components & Directives

| Symbol | Selector | Description | |---|---|---| | AnnotationStageComponent | <app-annotation-stage> | Global overlay — add once at app root | | AnnPanelComponent | <ann-panel> | Built-in floating panel with controls | | AnnotationCellDirective | [annCell], ann-cell-key | Makes any element an annotatable cell | | AnnotationKeyDirective | [annKey] | Marks an element by key (no pre-declared annotation) | | DotVisiblePipe | dotVisible | Pipe for conditional dot-badge visibility |


Annotation Types

| Type | Badge Color | Use case | |---|---|---| | info | Blue | Definitions, descriptions | | rule | Red-orange | Business rules, constraints | | comment | Green | General comments | | warning | Amber | Data quality issues, alerts | | formula | Purple | Calculation logic | | action | Indigo | Action items | | task | Dark blue | Tasks with to_do / in_progress / done status |


Permission Keys

The AnnPermissionDef list used by the built-in permission panel:

| Key | Description | |---|---| | add_new_dialog_custom | Create a new annotation | | edit_status | Change task status | | edit_title | Edit title | | edit_body | Edit body | | edit_tag | Edit tag | | edit_type | Change annotation type | | edit_assignee | Assign to a user | | edit_due_date | Set due date | | set_position_size | Move / resize the dialog | | delete_dialog_custom | Delete an annotation | | view_comment | View user comments | | send_comment | Post a user comment | | delete_comment | Delete a comment | | edit_permission | Manage page permissions | | view_logs | View activity logs | | set_anchor | Pin dialog anchor position |

Import the canonical list in your own code:

import { ANN_PERMISSION_LIST } from '@fastann/angular';
// ANN_PERMISSION_LIST: readonly AnnPermissionDef[]

License

MIT