easy-mat-ui-ng20 20.0.0 → 20.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,253 +1,253 @@
1
- # easy-mat-ui
2
-
3
- [![npm](https://img.shields.io/npm/v/easy-mat-ui-ng20.svg)](https://www.npmjs.com/package/easy-mat-ui-ng20)
4
- [![CI](https://github.com/chaibiredouane/easy-mat-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/chaibiredouane/easy-mat-ui/actions/workflows/ci.yml)
5
- [![Angular](https://img.shields.io/badge/Angular-20-dd0031.svg)](https://angular.dev)
6
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
-
8
- **[Live demo](__SITE_URL__/)** · **[Documentation](__SITE_URL__/docs/)** · [npm](https://www.npmjs.com/package/easy-mat-ui-ng20) · [Changelog](CHANGELOG.md)
9
-
10
- A set of customizable Angular Material components built around a configurable **data table**:
11
- filtering, sorting, pagination, typed columns, expandable rows, tabbed child tables, a toolbar,
12
- a configurable **create / edit / view dialog** and a **multi-select** control, with
13
- [ngx-translate](https://github.com/ngx-translate/core) based i18n.
14
-
15
- ## Features
16
-
17
- - `lib-easy-table` / `lib-easy-table-basic`: a Material table driven by a single `EasyTableConfig` object.
18
- - Typed columns (`MatColumnTyped`): text, number, date, date-time, icon, icon-and-text, button, dropdown list, image
19
- (flags), password and filter columns.
20
- - **Column filters** under the titles (a compact text input or a multi-select per column), global search, sorting,
21
- pagination, sticky columns and a header that stays pinned while the rows scroll.
22
- - Expandable rows with **child tables**, or several child tables in **tabs** (`childConfig`).
23
- - `EasyDialogService`: a configurable dialog to create, edit or view a record.
24
- - `lib-easy-multi-select`: a searchable multi-select with select-all, clear, read-only and compact modes.
25
- - **Light and dark themes** switchable at runtime (`EasyThemeService`), driven by CSS variables.
26
- - Responsive layout (tables adapt below 768 px and 1024 px) and no WCAG 2 A/AA violation reported by axe-core on the demo.
27
- - Shared models, enums and configuration classes exported from one entry point.
28
-
29
- ## Requirements and supported versions
30
-
31
- This package targets **Angular 20**. Each Angular major version has its own package so that applications
32
- are never forced to upgrade:
33
-
34
- | Angular | Package | Library version |
35
- |---|---|---|
36
- | 20 | `easy-mat-ui-ng20` | `20.x.x` |
37
- | 21 | `easy-mat-ui-ng21` | `21.x.x` (planned) |
38
-
39
- Peer dependencies: `@angular/core`, `@angular/common`, `@angular/forms`, `@angular/cdk`,
40
- `@angular/material`, `@angular/platform-browser` (all `^20.0.0`), `@ngx-translate/core` `^16`,
41
- `bootstrap` `^5`, `rxjs` `^7`. Optional: `@ngx-translate/http-loader`, `flag-icons` (country columns).
42
-
43
- ## Installation
44
-
45
- ```bash
46
- npm install easy-mat-ui-ng20
47
- ```
48
-
49
- Install the peer dependencies if your project does not have them yet:
50
-
51
- ```bash
52
- ng add @angular/material
53
- npm install @ngx-translate/core @ngx-translate/http-loader bootstrap
54
- ```
55
-
56
- The components use Material icons. `ng add @angular/material` normally takes care of it; otherwise add the
57
- font to your `index.html`:
58
-
59
- ```html
60
- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
61
- ```
62
-
63
- ### Bundle size budget
64
-
65
- A new Angular project fails `ng build` in production with *"bundle initial exceeded maximum budget"* once Angular Material
66
- and this library are added: two components embed the Bootstrap stylesheet (a slimmer stylesheet is planned for 20.1). Raise the
67
- initial budget in `angular.json` (measured: about 1.9 MB raw, 254 kB transferred, for the quick start below):
68
-
69
- ```json
70
- "budgets": [
71
- { "type": "initial", "maximumWarning": "2.5MB", "maximumError": "4MB" }
72
- ]
73
- ```
74
-
75
- ## Quick start
76
-
77
- Provide ngx-translate once in your application config:
78
-
79
- ```typescript
80
- import { ApplicationConfig } from '@angular/core';
81
- import { provideTranslateService } from '@ngx-translate/core';
82
-
83
- export const appConfig: ApplicationConfig = {
84
- providers: [provideTranslateService({ defaultLanguage: 'en' })]
85
- };
86
- ```
87
-
88
- Use the table in a standalone component:
89
-
90
- ```typescript
91
- import { Component } from '@angular/core';
92
- import { EasyTableComponent, EasyTableConfig, MatColumnTyped } from 'easy-mat-ui-ng20';
93
-
94
- @Component({
95
- selector: 'app-users',
96
- standalone: true,
97
- imports: [EasyTableComponent],
98
- template: `<lib-easy-table [config]="config"></lib-easy-table>`
99
- })
100
- export class UsersComponent {
101
- config: Partial<EasyTableConfig> = {
102
- data: [
103
- { id: 1, name: 'Alice' },
104
- { id: 2, name: 'Bob' }
105
- ],
106
- displayedColumns: [
107
- new MatColumnTyped({ name: 'id' }),
108
- new MatColumnTyped({ name: 'name' })
109
- ],
110
- canCreate: true,
111
- canUpdate: true,
112
- canDelete: true
113
- };
114
- }
115
- ```
116
-
117
- Table events are exposed as outputs: `addItem`, `editItem`, `viewItem`, `deleteItem`, `customAction`,
118
- `clickField`, and more (see `EasyTableComponent`).
119
-
120
- ## Dialog example
121
-
122
- ```typescript
123
- import { inject } from '@angular/core';
124
- import { CRUD, EasyDialogService, MatColumnTyped } from 'easy-mat-ui-ng20';
125
-
126
- const dialog = inject(EasyDialogService);
127
-
128
- dialog.open({
129
- title: 'Edit user',
130
- pk: 'id',
131
- action: CRUD.UPDATE,
132
- data: { id: 1, name: 'Alice' },
133
- fields: [
134
- new MatColumnTyped({ name: 'id', order: 1 }),
135
- new MatColumnTyped({ name: 'name', order: 2, isRequired: true })
136
- ]
137
- }).afterClosed().subscribe(result => console.log(result?.event, result?.data));
138
- ```
139
-
140
- ## Demo application
141
-
142
- The repository contains a runnable demo in [`projects/demo`](projects/demo). It needs no backend: a mock service
143
- (`GeoService`) returns Observables like a real `HttpClient` service would, so replacing it is the only change a real
144
- application needs.
145
-
146
- ```bash
147
- git clone https://github.com/chaibiredouane/easy-mat-ui.git
148
- cd easy-mat-ui
149
- npm ci
150
- npm start # http://localhost:4200
151
- ```
152
-
153
- It shows what the library is for: **almost nothing to code**. Every page is four small files, in the same layout:
154
-
155
- | File | Role |
156
- |---|---|
157
- | `<page>.config.ts` | The whole configuration: columns, dialog fields, table options, tabs |
158
- | `<page>.html` | A few lines: `<lib-easy-table [config]="..." [childConfig]="...">` and its events |
159
- | `<page>.ts` | Loads data from the service and saves the dialog results |
160
- | `<page>.scss` | Page styles |
161
-
162
- Three pages, built on every country of the world (Algeria first) and their cities:
163
-
164
- 1. **Countries and their cities**: expandable rows; the child table is loaded when a row opens.
165
- 2. **Child tables in tabs**: `childConfig` as an array, one tab per configuration.
166
- 3. **Cities of any country**: one full table with header filters, flags, status icons and a complete
167
- add / edit / view / delete dialog.
168
-
169
- Each page has a *Show the code of this page* panel with its real source files, and the header has a
170
- Light / Dark / System theme switcher.
171
-
172
- Key points of the configuration:
173
-
174
- - `childConfig` takes one configuration, or an array to get tabs (`tabTitle`, `tabIcon`).
175
- - `isExpanded: true` on the parent adds the expand button; `extendRow` tells you which row opened.
176
- - `sortedBy: ''` keeps the order given by your service (the default sorts by text).
177
- - Rebuild a table configuration (a new `EasyTableConfig`) whenever its data changes.
178
-
179
- The demo data lives under `projects/demo/src/app/data` and is **not** part of the npm package. Countries and cities
180
- come from [GeoNames](https://www.geonames.org/) (CC BY 4.0), the administrative divisions of Algeria from Wikipedia
181
- (CC BY-SA 4.0): see `DATA-LICENSE.md` and `WORLD-DATA-SOURCES.md` in that folder. Populations are source values of varying
182
- years, not a census.
183
-
184
- ## Themes
185
-
186
- Light and dark themes are driven by CSS variables (`--text-color`, `--card-bg`, `--border-color`,
187
- `--color-primary-blue`, `--radius-md`, `--shadow-md`, ...), so an application can override them without touching the
188
- library. `EasyThemeService` switches theme at runtime, without reloading the page:
189
-
190
- ```typescript
191
- import { inject } from '@angular/core';
192
- import { EasyThemeService } from 'easy-mat-ui-ng20';
193
-
194
- const theme = inject(EasyThemeService);
195
- theme.setTheme('dark'); // 'light' | 'dark' | 'system' (follows the operating system, live)
196
- theme.toggle();
197
- theme.theme(); // the applied theme, as a signal
198
- ```
199
-
200
- The service does nothing until you call it, never stores anything unless you give it a `storageKey`
201
- (`EASY_THEME_OPTIONS`), and only adds the `dark-theme` class to `<body>` (the class the library stylesheet already
202
- used) and a `data-easy-theme` attribute on `<html>`. Applications that toggle `dark-theme` themselves keep working.
203
- Material components follow when your Angular Material theme uses `color-scheme` (for example `mat.theme`).
204
-
205
- ## Responsive and accessibility
206
-
207
- Below 768 px, tables size their columns to their content and scroll horizontally, and the sticky actions column shrinks
208
- to its buttons so the data stays visible. The demo is checked at 320, 375, 430, 768, 1024, 1440 and 1920 px in both
209
- themes (no horizontal overflow, no console error) and with axe-core (no WCAG 2 A/AA violation on the demo pages).
210
-
211
- ## Styles
212
-
213
- Components embed their own styles. To also get the shared classes (`green`, `red`, `taupe`, `bg-*`,
214
- spacing and Material overrides), add the stylesheet to the `styles` array of your `angular.json`:
215
-
216
- ```json
217
- "styles": [
218
- "node_modules/easy-mat-ui-ng20/styles/styles.scss",
219
- "node_modules/flag-icons/css/flag-icons.min.css"
220
- ]
221
- ```
222
-
223
- `flag-icons` is only needed for flag columns (`ColumnType.IMAGE` with a country code such as `fr`); its stylesheet is not bundled with the components.
224
-
225
- > **Troubleshooting:** with the Angular `application` builder, the `flag-icons` stylesheet can fail in
226
- > development with `Two output files share the same path but have different contents: media/xx.svg`.
227
- > Set `"outputHashing": "media"` in the `development` configuration of the `build` target of your
228
- > `angular.json`.
229
-
230
- ## Versioning
231
-
232
- The first number of the library version is the supported Angular major version (`20.x.x` supports
233
- Angular 20). The remaining numbers follow [Semantic Versioning](https://semver.org/):
234
- PATCH for bug fixes, MINOR for backward-compatible features, MAJOR for breaking changes within the
235
- same Angular line. See [CHANGELOG.md](CHANGELOG.md).
236
-
237
- ## Development
238
-
239
- ```bash
240
- npm ci
241
- npm start # runs the demo application
242
- npm run build # builds the library into dist/easy-mat-ui
243
- npm test # runs the unit tests (headless Chrome)
244
- ```
245
-
246
- ## Contributing
247
-
248
- Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) and the
249
- [Code of Conduct](CODE_OF_CONDUCT.md). Report vulnerabilities as described in [SECURITY.md](SECURITY.md).
250
-
251
- ## License
252
-
253
- [MIT](LICENSE)
1
+ # easy-mat-ui
2
+
3
+ [![npm](https://img.shields.io/npm/v/easy-mat-ui-ng20.svg)](https://www.npmjs.com/package/easy-mat-ui-ng20)
4
+ [![CI](https://github.com/chaibiredouane/easy-mat-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/chaibiredouane/easy-mat-ui/actions/workflows/ci.yml)
5
+ [![Angular](https://img.shields.io/badge/Angular-20-dd0031.svg)](https://angular.dev)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ **[Live demo](__SITE_URL__/)** · **[Documentation](__SITE_URL__/docs/)** · [npm](https://www.npmjs.com/package/easy-mat-ui-ng20) · [Changelog](CHANGELOG.md)
9
+
10
+ A set of customizable Angular Material components built around a configurable **data table**:
11
+ filtering, sorting, pagination, typed columns, expandable rows, tabbed child tables, a toolbar,
12
+ a configurable **create / edit / view dialog** and a **multi-select** control, with
13
+ [ngx-translate](https://github.com/ngx-translate/core) based i18n.
14
+
15
+ ## Features
16
+
17
+ - `lib-easy-table` / `lib-easy-table-basic`: a Material table driven by a single `EasyTableConfig` object.
18
+ - Typed columns (`MatColumnTyped`): text, number, date, date-time, icon, icon-and-text, button, dropdown list, image
19
+ (flags), password and filter columns.
20
+ - **Column filters** under the titles (a compact text input or a multi-select per column), global search, sorting,
21
+ pagination, sticky columns and a header that stays pinned while the rows scroll.
22
+ - Expandable rows with **child tables**, or several child tables in **tabs** (`childConfig`).
23
+ - `EasyDialogService`: a configurable dialog to create, edit or view a record.
24
+ - `lib-easy-multi-select`: a searchable multi-select with select-all, clear, read-only and compact modes, or a single-choice dropdown (`single`).
25
+ - **Light and dark themes** switchable at runtime (`EasyThemeService`), driven by CSS variables.
26
+ - Responsive layout (tables adapt below 768 px and 1024 px) and no WCAG 2 A/AA violation reported by axe-core on the demo.
27
+ - Shared models, enums and configuration classes exported from one entry point.
28
+
29
+ ## Requirements and supported versions
30
+
31
+ This package targets **Angular 20**. Each Angular major version has its own package so that applications
32
+ are never forced to upgrade:
33
+
34
+ | Angular | Package | Library version |
35
+ |---|---|---|
36
+ | 20 | `easy-mat-ui-ng20` | `20.x.x` |
37
+ | 21 | `easy-mat-ui-ng21` | `21.x.x` (planned) |
38
+
39
+ Peer dependencies: `@angular/core`, `@angular/common`, `@angular/forms`, `@angular/cdk`,
40
+ `@angular/material`, `@angular/platform-browser` (all `^20.0.0`), `@ngx-translate/core` `^16`,
41
+ `bootstrap` `^5`, `rxjs` `^7`. Optional: `@ngx-translate/http-loader`, `flag-icons` (country columns).
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ npm install easy-mat-ui-ng20
47
+ ```
48
+
49
+ Install the peer dependencies if your project does not have them yet:
50
+
51
+ ```bash
52
+ ng add @angular/material
53
+ npm install @ngx-translate/core @ngx-translate/http-loader bootstrap
54
+ ```
55
+
56
+ The components use Material icons. `ng add @angular/material` normally takes care of it; otherwise add the
57
+ font to your `index.html`:
58
+
59
+ ```html
60
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
61
+ ```
62
+
63
+ ### Bundle size budget
64
+
65
+ A new Angular project fails `ng build` in production with *"bundle initial exceeded maximum budget"* once Angular Material
66
+ and this library are added: two components embed the Bootstrap stylesheet (a slimmer stylesheet is planned for 20.1). Raise the
67
+ initial budget in `angular.json` (measured: about 1.9 MB raw, 254 kB transferred, for the quick start below):
68
+
69
+ ```json
70
+ "budgets": [
71
+ { "type": "initial", "maximumWarning": "2.5MB", "maximumError": "4MB" }
72
+ ]
73
+ ```
74
+
75
+ ## Quick start
76
+
77
+ Provide ngx-translate once in your application config:
78
+
79
+ ```typescript
80
+ import { ApplicationConfig } from '@angular/core';
81
+ import { provideTranslateService } from '@ngx-translate/core';
82
+
83
+ export const appConfig: ApplicationConfig = {
84
+ providers: [provideTranslateService({ defaultLanguage: 'en' })]
85
+ };
86
+ ```
87
+
88
+ Use the table in a standalone component:
89
+
90
+ ```typescript
91
+ import { Component } from '@angular/core';
92
+ import { EasyTableComponent, EasyTableConfig, MatColumnTyped } from 'easy-mat-ui-ng20';
93
+
94
+ @Component({
95
+ selector: 'app-users',
96
+ standalone: true,
97
+ imports: [EasyTableComponent],
98
+ template: `<lib-easy-table [config]="config"></lib-easy-table>`
99
+ })
100
+ export class UsersComponent {
101
+ config: Partial<EasyTableConfig> = {
102
+ data: [
103
+ { id: 1, name: 'Alice' },
104
+ { id: 2, name: 'Bob' }
105
+ ],
106
+ displayedColumns: [
107
+ new MatColumnTyped({ name: 'id' }),
108
+ new MatColumnTyped({ name: 'name' })
109
+ ],
110
+ canCreate: true,
111
+ canUpdate: true,
112
+ canDelete: true
113
+ };
114
+ }
115
+ ```
116
+
117
+ Table events are exposed as outputs: `addItem`, `editItem`, `viewItem`, `deleteItem`, `customAction`,
118
+ `clickField`, and more (see `EasyTableComponent`).
119
+
120
+ ## Dialog example
121
+
122
+ ```typescript
123
+ import { inject } from '@angular/core';
124
+ import { CRUD, EasyDialogService, MatColumnTyped } from 'easy-mat-ui-ng20';
125
+
126
+ const dialog = inject(EasyDialogService);
127
+
128
+ dialog.open({
129
+ title: 'Edit user',
130
+ pk: 'id',
131
+ action: CRUD.UPDATE,
132
+ data: { id: 1, name: 'Alice' },
133
+ fields: [
134
+ new MatColumnTyped({ name: 'id', order: 1 }),
135
+ new MatColumnTyped({ name: 'name', order: 2, isRequired: true })
136
+ ]
137
+ }).afterClosed().subscribe(result => console.log(result?.event, result?.data));
138
+ ```
139
+
140
+ ## Demo application
141
+
142
+ The repository contains a runnable demo in [`projects/demo`](projects/demo). It needs no backend: a mock service
143
+ (`GeoService`) returns Observables like a real `HttpClient` service would, so replacing it is the only change a real
144
+ application needs.
145
+
146
+ ```bash
147
+ git clone https://github.com/chaibiredouane/easy-mat-ui.git
148
+ cd easy-mat-ui
149
+ npm ci
150
+ npm start # http://localhost:4200
151
+ ```
152
+
153
+ It shows what the library is for: **almost nothing to code**. Every page is four small files, in the same layout:
154
+
155
+ | File | Role |
156
+ |---|---|
157
+ | `<page>.config.ts` | The whole configuration: columns, dialog fields, table options, tabs |
158
+ | `<page>.html` | A few lines: `<lib-easy-table [config]="..." [childConfig]="...">` and its events |
159
+ | `<page>.ts` | Loads data from the service and saves the dialog results |
160
+ | `<page>.scss` | Page styles |
161
+
162
+ Three pages, built on every country of the world (Algeria first) and their cities:
163
+
164
+ 1. **Countries and their cities**: expandable rows; the child table is loaded when a row opens.
165
+ 2. **Child tables in tabs**: `childConfig` as an array, one tab per configuration.
166
+ 3. **Cities of any country**: one full table with header filters, flags, status icons and a complete
167
+ add / edit / view / delete dialog.
168
+
169
+ Each page has a *Show the code of this page* panel with its real source files, and the header has a
170
+ Light / Dark / System theme switcher.
171
+
172
+ Key points of the configuration:
173
+
174
+ - `childConfig` takes one configuration, or an array to get tabs (`tabTitle`, `tabIcon`).
175
+ - `isExpanded: true` on the parent adds the expand button; `extendRow` tells you which row opened.
176
+ - `sortedBy: ''` keeps the order given by your service (the default sorts by text).
177
+ - Rebuild a table configuration (a new `EasyTableConfig`) whenever its data changes.
178
+
179
+ The demo data lives under `projects/demo/src/app/data` and is **not** part of the npm package. Countries and cities
180
+ come from [GeoNames](https://www.geonames.org/) (CC BY 4.0), the administrative divisions of Algeria from Wikipedia
181
+ (CC BY-SA 4.0): see `DATA-LICENSE.md` and `WORLD-DATA-SOURCES.md` in that folder. Populations are source values of varying
182
+ years, not a census.
183
+
184
+ ## Themes
185
+
186
+ Light and dark themes are driven by CSS variables (`--text-color`, `--card-bg`, `--border-color`,
187
+ `--color-primary-blue`, `--radius-md`, `--shadow-md`, ...), so an application can override them without touching the
188
+ library. `EasyThemeService` switches theme at runtime, without reloading the page:
189
+
190
+ ```typescript
191
+ import { inject } from '@angular/core';
192
+ import { EasyThemeService } from 'easy-mat-ui-ng20';
193
+
194
+ const theme = inject(EasyThemeService);
195
+ theme.setTheme('dark'); // 'light' | 'dark' | 'system' (follows the operating system, live)
196
+ theme.toggle();
197
+ theme.theme(); // the applied theme, as a signal
198
+ ```
199
+
200
+ The service does nothing until you call it, never stores anything unless you give it a `storageKey`
201
+ (`EASY_THEME_OPTIONS`), and only adds the `dark-theme` class to `<body>` (the class the library stylesheet already
202
+ used) and a `data-easy-theme` attribute on `<html>`. Applications that toggle `dark-theme` themselves keep working.
203
+ Material components follow when your Angular Material theme uses `color-scheme` (for example `mat.theme`).
204
+
205
+ ## Responsive and accessibility
206
+
207
+ Below 768 px, tables size their columns to their content and scroll horizontally, and the sticky actions column shrinks
208
+ to its buttons so the data stays visible. The demo is checked at 320, 375, 430, 768, 1024, 1440 and 1920 px in both
209
+ themes (no horizontal overflow, no console error) and with axe-core (no WCAG 2 A/AA violation on the demo pages).
210
+
211
+ ## Styles
212
+
213
+ Components embed their own styles. To also get the shared classes (`green`, `red`, `taupe`, `bg-*`,
214
+ spacing and Material overrides), add the stylesheet to the `styles` array of your `angular.json`:
215
+
216
+ ```json
217
+ "styles": [
218
+ "node_modules/easy-mat-ui-ng20/styles/styles.scss",
219
+ "node_modules/flag-icons/css/flag-icons.min.css"
220
+ ]
221
+ ```
222
+
223
+ `flag-icons` is only needed for flag columns (`ColumnType.IMAGE` with a country code such as `fr`); its stylesheet is not bundled with the components.
224
+
225
+ > **Troubleshooting:** with the Angular `application` builder, the `flag-icons` stylesheet can fail in
226
+ > development with `Two output files share the same path but have different contents: media/xx.svg`.
227
+ > Set `"outputHashing": "media"` in the `development` configuration of the `build` target of your
228
+ > `angular.json`.
229
+
230
+ ## Versioning
231
+
232
+ The first number of the library version is the supported Angular major version (`20.x.x` supports
233
+ Angular 20). The remaining numbers follow [Semantic Versioning](https://semver.org/):
234
+ PATCH for bug fixes, MINOR for backward-compatible features, MAJOR for breaking changes within the
235
+ same Angular line. See [CHANGELOG.md](CHANGELOG.md).
236
+
237
+ ## Development
238
+
239
+ ```bash
240
+ npm ci
241
+ npm start # runs the demo application
242
+ npm run build # builds the library into dist/easy-mat-ui
243
+ npm test # runs the unit tests (headless Chrome)
244
+ ```
245
+
246
+ ## Contributing
247
+
248
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) and the
249
+ [Code of Conduct](CODE_OF_CONDUCT.md). Report vulnerabilities as described in [SECURITY.md](SECURITY.md).
250
+
251
+ ## License
252
+
253
+ [MIT](LICENSE)