This file is the single canonical repository context for every coding agent (Claude Code, Cursor, Codex, JetBrains AI Assistant, Copilot, …). Tool-specific files, if any, should only point here. Shared agent skills live in .agents/skills/. If instructions conflict, prefer this file and the actual codebase.
ng-select is a widely consumed Angular 22 component library (select, multiselect, autocomplete) published to npm. Preserve public API compatibility, accessibility, keyboard navigation, forms integration, and OnPush-friendly data flow. Breaking changes require deliberate versioning and release notes. Inspect nearby code first and prefer established local patterns over new abstractions.
| Package | Path | Purpose |
|---|---|---|
@ng-select/ng-select |
src/ng-select/ |
Main select/multiselect/autocomplete component |
@ng-select/ng-option-highlight |
src/ng-option-highlight/ |
NgOptionHighlightDirective ([ngOptionHighlight]): highlights the search term in option labels |
| docs site | website/ |
Astro + Starlight; demos in src/demo/app/examples/ render as Angular islands via @analogjs/astro-angular |
- Stack: Angular 22, TypeScript 6,
@angular/cdk(dropdown overlay), ng-packagr (@angular/build:ng-packagr), Vitest browser mode, ESLint 10 + angular-eslint, Prettier, pnpm. - Public API:
src/ng-select/public-api.ts,src/ng-option-highlight/public-api.ts. User docs:README.mdandwebsite/src/content/docs/reference/api.mdx. - Versions: semantic-release versions from git tags. v24.x (CDK Overlay) and v23.x (no Overlay from 23.11.0; 23.7.0–23.10.0 shipped it by mistake) both target Angular
^22.0.0. Theversioninsrc/*/package.jsonis not bumped in-repo; runpnpm view @ng-select/ng-select versionfor the latest release. Compatibility matrix:website/src/content/docs/reference/versions.mdxand README. - Peers:
@angular/{cdk,common,core,forms}^22.0.0(ng-option-highlight:common,core). - Links: docs · repository
- pnpm only. Never npm or yarn.
- Public API: check
public-api.ts, README, andreference/api.mdxbefore adding, renaming, or removing public members, and document new user-facing inputs/outputs there. Keep inputs, outputs, CSS class names, and DOM structure backward compatible. - OnPush: consumers must replace
items/model arrays rather than mutate them; library code uses immutable updates too. - Standalone: all components and directives are standalone. Keep
NgSelectModulefor backward compatibility; never add new NgModules. - Reuse existing helpers (
utils/value-utils.ts,utils/search-helper.ts,items-list.ts,selection-model.ts); search for similar code before adding helpers. - Dependencies: no new runtime dependencies in published packages without strong justification.
- Generated files: never hand-edit
dist/, coverage output, orllms.txt/llms-full.txt(regenerated bypnpm build:llmsfrom the MDX docs and examples). - User-visible behavior: add or update a demo example.
- Commits: Conventional Commits; semantic-release derives versions from them.
- Attribution: DO NOT list yourself, your model name, or any AI/LLM as author, co-author, contributor, or credit anywhere: files, changelogs, comments, docs, commit messages (including
Co-authored-bytrailers), PR titles or bodies.
- Execute implementation with Opus (medium effort) or Sonnet agents, parallelizing independent work aggressively for speed. Use up to 100 agents for max speed.
- Use as many parallel agents as useful without duplicating work or making conflicting edits: give each a non-overlapping file scope and a complete brief (files, constraints from this file, done criteria).
pnpm install
pnpm start # docs site at http://localhost:4300/ng-select/
pnpm build # both libraries + compiled themes + SCSS copies → dist/
pnpm build:docs # llms.txt + Astro build → dist/docs
pnpm docs:preview # serve the built docs site
pnpm test # both libraries, zoneless, with coverage
pnpm test:watch # ng-select only, watch mode
pnpm test:zone # both libraries on the zone.js lane
pnpm test:ci # what CI runs: zoneless + coverage, then zone.js lane
pnpm exec ng test ng-select --watch=false # fast single-library run
pnpm lint # ESLint, both libraries
pnpm format # Prettier write (format:check to verify)src/ng-select/
├── lib/
│ ├── ng-select/ # NgSelectComponent (.ts/.html/.scss) + focused specs
│ ├── dropdown-panel/ # NgDropdownPanelComponent/Service (virtual-scroll range), CDK overlay manager + container, positioning
│ ├── directives/ # ng-templates.directive.ts: ng-*-tmp template directives, ngItemLabel
│ ├── services/ # NgSelectConfig (global defaults), ConsoleService
│ ├── types/ # ng-select.types.ts, id.ts
│ ├── utils/ # search-helper.ts, value-utils.ts
│ ├── ng-option.component.ts
│ ├── items-list.ts # filtering, grouping, marking, selected options
│ ├── selection-model.ts # SelectionModel, DefaultSelectionModel(Factory)
│ └── ng-select.module.ts # legacy NgSelectModule
├── themes/ # default, material, ant.design (+ _mixins.scss)
├── testing/ # helpers, mocks, fixtures, timer helpers
└── public-api.ts
src/ng-option-highlight/lib/ # ng-option-highlight.directive.ts
src/demo/app/examples/ # one folder per demo (no app shell)
website/
├── astro.config.mjs # base /ng-select, outDir ../dist/docs
└── src/
├── content/docs/{getting-started,reference,examples}/*.mdx
├── components/ # Demo.astro, ThemeSelect.astro (theme switcher)
├── angular/ # demo-host.component.ts, examples.registry.ts, hero-select.component.ts
└── stackblitz/ # StackBlitz starter files
scripts/generate-llms.mjs
.github/workflows/ # ci, release, codeql, stale, deprecate, dependabot
- Embed a demo in MDX:
import Demo from '@components/Demo.astro';then<Demo example="<folder>" />. - New example:
src/demo/app/examples/<name>-example/<name>-example.component.{ts,html,scss}, class<PascalCase(name)>ExampleComponent, selectorng-<name>-example, then add a line to the manual registrywebsite/src/angular/examples.registry.ts. - Each demo is its own Angular app. Demo
NgSelectConfigdefaults come fromDEMO_PROVIDERSindemo-host.component.ts, not a shared root injector.
-
Target Angular 22 / TypeScript 6 APIs; no deprecated APIs.
-
New code:
input(),output(),model(),signal(),computed(),effect(),linkedSignal(),inject(), and control flow (@if,@for,@switch). No new@Input/@Output; don't rewrite unrelated legacy code. -
NgSelectComponentinputs use a backward-compatible pattern. Follow it when adding inputs:readonly _placeholder = input<string>(this.config.placeholder, { alias: 'placeholder' }); readonly placeholder = linkedSignal(() => this._placeholder());
bindLabel,bindValue,appearance,isOpen, anditemsaremodel()s; outputs useoutput({ alias }). Give inputs an explicit type and, for booleans,transform: booleanAttribute. -
DI:
providedIn: 'root'unless scoped (NgDropdownPanelServiceis provided perNgSelectComponent). Global defaults viaNgSelectConfig; selection override via theSELECTION_MODEL_FACTORYtoken (defined inng-select.component.ts). -
Selectors (ESLint-enforced): elements
ng-*kebab-case, attribute directivesng*camelCase. -
Templates: preserve content projection and the template directives (
ng-option-tmp,ng-label-tmp, …). -
Types: explicit types in new code; avoid
any(ESLint allows it).tsconfig.jsonisstrict: false, so match the surrounding file. -
Libraries never do HTTP or hold app state; keep data loading (
HttpClient, RxJS) in demo examples. -
JSDoc public inputs, outputs, and methods.
- Forms: one
NG_VALUE_ACCESSORservesngModel, reactive forms, and signal forms ([formField]via CVA interop). All three must keep working in single and multiple mode, includingdisabled/readonlyand validation classes (ng-invalid,ng-touched). - Selection: default logic in
selection-model.ts, overridable viaSELECTION_MODEL_FACTORY. Don't change default semantics without single + multiple tests. - Dropdown: the panel renders in a CDK Overlay anchored to the select.
appendTocontrols DOM containment only (positioning stays viewport-based);popoveris a deprecated no-op.NgDropdownPanelServicedoes virtual-scroll measurement. - Accessibility: keyboard navigation (arrows, Enter, Escape, Tab, Backspace), focus management, and ARIA (
ariaLabel,ariaLabelDropdown,labelForId, aria-live status) are core; regressions are high severity. ESLint enablestemplateAccessibility. - Virtual/infinite scroll interacts with
ItemsListand scroll events; test with large datasets. - Typeahead:
typeaheadis a consumer-ownedSubject<string>; the library emits terms onceminTermLengthis met and never debounces.
src/ng-select/themes/*.theme.scss→dist/ng-select/themes/*.theme.css(build:themes); SCSS copied todist/ng-select/scss/(copy-sass).ViewEncapsulation.None: styles are global and theme class names are part of the public styling contract.- When changing styles, verify all three themes (docs site theme switcher) and validation-state styling.
- Vitest browser mode: headless Chromium via
@vitest/browser-playwright, wired through@angular/build:unit-test+vitest.config.ts. Real layout, not jsdom. No e2e suite. - Zoneless by default; the
test-zonetarget (pnpm test:zone, part oftest:ci) reruns specs with zone.js. - Timers:
vi.useFakeTimersviatesting/timer-helpers.ts(enableDebounceFakeTimers,advanceDebounce,openSelect), notfakeAsync/tick. Other helpers:testing/helpers.ts(tickAndDetectChanges,selectOption,triggerKeyDownEvent, …),testing/mocks.ts. - Specs sit next to code.
NgSelectComponentbehavior specs are split by area inlib/ng-select/(ng-select.<area>.spec.ts: forms, signal-forms, selection, keyboard, overlay, events, templates, accessibility); extend the matching one. - Failed specs save screenshots to a
__screenshots__/folder next to the spec. - Don't add tests unless requested or needed to cover changed behavior; update existing specs when behavior changes.
- Trivial/medium changes:
pnpm lint+pnpm build. - Library behavior: also
pnpm exec ng test ng-select --watch=false(orpnpm test:ci). - Docs/examples:
pnpm build:docs. - Formatting:
pnpm exec prettier --check <changed files>. - Report skipped or failed checks; call out public API, accessibility, theming, and coverage impacts; update README/docs/examples when user-facing behavior or API changed.
- Runtime verification recipe:
.agents/skills/verify.
ci.yml(every PR; pushes touchingts,html,scss,mdx,mjs,astro,website/**): install → Playwright Chromium →lint→test:ci→build:docs→ Coveralls.release.yml(push tomaster):pnpm build→ semantic-release publishes@ng-select/ng-selectfromdist/ng-select→ version-synced@ng-select/ng-option-highlight→build:docs→ deploydist/docstogh-pages.
pnpm-workspace.yamlpatchedDependenciespatches@analogjs/astro-angular(patches/); re-check the patch when upgrading it.tsconfig.jsonpath aliases map@ng-select/ng-select,@ng-select/ng-option-highlight, and@examples/*to source.- IDEs: use pnpm, the workspace
eslint.config.js, and Prettier (.prettierrc.json).