Files
start-os/web/CLAUDE.md
Matt Hill d97ab59bab update bindings for API types, add ARCHITECTURE (#3124)
* update binding for API types, add ARCHITECTURE

* translations
2026-02-16 16:23:28 +01:00

3.7 KiB

Web — Angular Frontend

Angular 20 + TypeScript workspace using Taiga UI component library.

Projects

  • projects/ui/ — Main admin interface
  • projects/setup-wizard/ — Initial setup
  • projects/start-tunnel/ — VPN management UI
  • projects/shared/ — Common library (API clients, components, i18n)
  • projects/marketplace/ — Service discovery

Development

npm ci
npm run start:ui        # Dev server with mocks
npm run build:ui        # Production build
npm run check           # Type check all projects

Golden Rules

  1. Taiga-first. Use Taiga components, directives, and APIs whenever possible. Avoid hand-rolled HTML/CSS unless absolutely necessary. If Taiga has a component for it, use it.

  2. Pattern-match. Nearly anything we build has a similar example elsewhere in this codebase. Search for existing patterns before writing new code. Copy the conventions used in neighboring components.

  3. When unsure about Taiga, ask or look it up. Use WebFetch against https://taiga-ui.dev/llms-full.txt to search for component usage, or ask the user. Taiga docs are authoritative. See Taiga UI Docs below.

Taiga UI Docs

Taiga provides an LLM-friendly reference at https://taiga-ui.dev/llms-full.txt (~2200 lines covering all components with code examples). Use WebFetch to search it when you need to look up a component, directive, or API:

WebFetch url=https://taiga-ui.dev/llms-full.txt prompt="How to use TuiTextfield with a select dropdown"

When implementing something with Taiga, also check existing code in this project for local patterns and conventions — Taiga usage here may have project-specific wrappers or style choices.

Architecture

See ARCHITECTURE.md for the web architecture: API layer, PatchDB state, WebSockets, routing, forms, i18n, and services.

Component Conventions

  • Standalone components preferred (no NgModule). Use imports array in @Component.
  • export default class for route components (enables direct loadComponent import).
  • inject() function for DI (not constructor injection).
  • signal() and computed()** for local reactive state.
  • toSignal() to convert Observables (e.g., PatchDB watches) to signals.
  • ChangeDetectionStrategy.OnPush on almost all components.
  • takeUntilDestroyed(inject(DestroyRef)) for subscription cleanup.

Common Taiga Patterns

Textfield + Select (dropdown)

<tui-textfield tuiChevron>
  <label tuiLabel>Label</label>
  <input tuiSelect />
  <tui-data-list *tuiTextfieldDropdown>
    <button tuiOption [value]="item" *ngFor="let item of items">{{ item }}</button>
  </tui-data-list>
</tui-textfield>

Provider to remove the X clear button:

providers: [tuiTextfieldOptionsProvider({ cleaner: signal(false) })]

Buttons

<button tuiButton appearance="primary">Submit</button>
<button tuiButton appearance="secondary">Cancel</button>
<button tuiIconButton appearance="icon" iconStart="@tui.trash"></button>

Dialogs

// Confirmation
this.dialog.openConfirm({ label: 'Warning', data: { content: '...', yes: 'Confirm', no: 'Cancel' } })

// Custom component in dialog
this.dialog.openComponent(new PolymorpheusComponent(MyComponent, injector), { label: 'Title' })

Toggle

<input tuiSwitch type="checkbox" size="m" [showIcons]="false" [(ngModel)]="value" />

Errors & Tooltips

<tui-error [error]="[] | tuiFieldError | async" />
<tui-icon [tuiTooltip]="'Hint text'" />

Layout

<tui-elastic-container><!-- dynamic height --></tui-elastic-container>
<tui-scrollbar><!-- scrollable content --></tui-scrollbar>
<tui-loader [textContent]="'Loading...' | i18n" />