@aastrika/ai-elements 0.1.0 → 0.2.0

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,16 +1,101 @@
1
1
  # @aastrika/ai-elements
2
2
 
3
- Three Aastrika features as custom elements. Works in React, Vue, Angular, Svelte
4
- or plain HTML.
3
+ **Three Aastrika features as custom elements.** Works in React, Vue, Angular,
4
+ Svelte or plain HTML.
5
5
 
6
+ | Tag | What it does |
7
+ |---|---|
8
+ | `<aastrika-content-studio>` | A document becomes a narrated, illustrated training video |
9
+ | `<aastrika-assessment>` | Source material becomes a validated MCQ set |
10
+ | `<aastrika-reports>` | Who used what, and what it cost *(admin only)* |
11
+
12
+ One file, **86 kB** over the wire, **no dependencies**. Angular is compiled
13
+ inside, so you never install it and never upgrade because we did.
14
+
15
+ ---
16
+
17
+ ## Contents
18
+
19
+ | | |
20
+ |---|---|
21
+ | [The three features](#the-three-features) | What each element does |
22
+ | [Setup](#setup) | Install, access, configure, mount |
23
+ | [Framework notes](#framework-notes) | React · Angular · Plain HTML |
24
+ | [Configuration](#configuration) | The three values, and why two are functions |
25
+ | [Access](#access) | Which gateway groups each feature needs |
26
+ | [Theming](#theming) | CSS custom properties |
27
+ | [Knowing when something failed](#knowing-when-something-failed) | Routing errors to your monitoring |
28
+ | [Inputs and events](#inputs-and-events) | Opening the form set up, and acting on the result |
29
+ | [When it does not work](#when-it-does-not-work) | Symptom → cause |
30
+ | [Browser support](#browser-support) · [Server rendering](#server-rendering) | Constraints |
31
+
32
+ ---
33
+
34
+ ## Quick start
35
+
36
+ ```bash
37
+ npm i @aastrika/ai-elements
6
38
  ```
7
- aastrika-content-studio a document becomes a narrated, illustrated training video
8
- aastrika-assessment source material becomes a validated MCQ set
9
- aastrika-reports who used what, and what it cost (admin only)
39
+
40
+ ```js
41
+ import { configure } from '@aastrika/ai-elements';
42
+
43
+ configure({
44
+ apiBase: 'https://api.aastrika.org/ai-studio',
45
+ getAuthHeaders: async () => ({ Authorization: `Bearer ${await auth.token()}` }),
46
+ creator: () => currentUser.username,
47
+ });
48
+ ```
49
+
50
+ ```html
51
+ <aastrika-content-studio></aastrika-content-studio>
10
52
  ```
11
53
 
12
- One file, 86 kB over the wire, no dependencies. Angular is compiled inside, so
13
- you never install it and never upgrade because we did.
54
+ That is the whole integration. The rest of this file is detail.
55
+
56
+ > **You also need gateway access.** The package authenticates nobody — see
57
+ > [Access](#access) for the groups each feature requires.
58
+
59
+ ---
60
+
61
+ ## How it fits together
62
+
63
+ ```mermaid
64
+ flowchart LR
65
+ subgraph host["Your application"]
66
+ cfg["configure()"]
67
+ tag["&lt;aastrika-content-studio&gt;"]
68
+ end
69
+
70
+ subgraph pkg["@aastrika/ai-elements"]
71
+ el["Custom element"]
72
+ int["Interceptor"]
73
+ end
74
+
75
+ subgraph svc["Aastrika"]
76
+ gw["Gateway<br/>checks the token"]
77
+ api["AI service"]
78
+ end
79
+
80
+ cfg -.->|apiBase · auth · creator| int
81
+ tag --> el
82
+ el --> int
83
+ int -->|"Authorization<br/>x-aastrika-creator"| gw
84
+ gw --> api
85
+ api -.->|JSON| el
86
+ el -.->|onError| cfg
87
+
88
+ classDef a fill:#0F766E,stroke:none,color:#fff
89
+ classDef b fill:#f1f5f4,stroke:#cbd5d3,color:#16181d
90
+ class el,int a
91
+ class cfg,tag,gw,api b
92
+ ```
93
+
94
+ **Your application supplies three values. The package does the rest** — builds
95
+ every request from `apiBase`, calls your two functions on each one, and renders
96
+ into your page, styled through CSS custom properties.
97
+
98
+ ---
14
99
 
15
100
  ## The three features
16
101
 
@@ -50,63 +135,113 @@ apart.
50
135
 
51
136
  [Full walkthrough →](features/reports/README.md)
52
137
 
138
+ ---
139
+
53
140
  ## Setup
54
141
 
55
- ### 1. Install
142
+ The [Quick start](#quick-start) above is the whole integration. What each step
143
+ is actually doing:
56
144
 
57
- ```bash
58
- npm i @aastrika/ai-elements
145
+ | | Step | Notes |
146
+ |---|---|---|
147
+ | **1** | `npm i @aastrika/ai-elements` | No peer dependencies, no framework to match. Any bundler, or none |
148
+ | **2** | Get gateway access | The package authenticates nobody — your gateway decides. See [Access](#access), then ask the Aastrika platform team |
149
+ | **3** | `configure({ ... })` | Once, before the first element renders. Two of the three values are **functions** — see [Configuration](#configuration) |
150
+ | **4** | Write the tag | Registration happens on import, so there is no third step |
151
+
152
+ ---
153
+
154
+ ## Framework notes
155
+
156
+ ### React
157
+
158
+ ```jsx
159
+ import '@aastrika/ai-elements';
160
+
161
+ <aastrika-content-studio />
59
162
  ```
60
163
 
61
- No peer dependencies, no framework to match. Any bundler, or none.
164
+ React 19 passes properties and custom events natively — no wrapper, no `ref`.
62
165
 
63
- ### 2. Get gateway access
166
+ ### Angular
64
167
 
65
- The package authenticates nobody. Your application is already signed in, and the
66
- Aastrika gateway decides whether a call is allowed — so before anything works
67
- you need a Kong consumer with the right groups. See [Access](#access) for which
68
- feature needs which, and ask the Aastrika platform team to set it up.
168
+ Two steps: call `configure()` once at bootstrap, then add
169
+ `CUSTOM_ELEMENTS_SCHEMA` to whichever component writes the tag.
69
170
 
70
- ### 3. Configure, once, at startup
171
+ **1. Configure at bootstrap** — `main.ts`, or an `APP_INITIALIZER` if the values
172
+ come from something you have to load first:
71
173
 
72
- ```js
174
+ ```ts
73
175
  import { configure } from '@aastrika/ai-elements';
176
+ import { environment } from './environments/environment';
74
177
 
75
178
  configure({
76
- apiBase: 'https://api.aastrika.org/ai-studio',
77
- getAuthHeaders: async () => ({ Authorization: `Bearer ${await auth.token()}` }),
78
- creator: () => currentUser.username,
179
+ apiBase: environment.aastrikaApiBase,
180
+ getAuthHeaders: async () => ({ Authorization: `Bearer ${await authService.token()}` }),
181
+ creator: () => authService.username(),
79
182
  });
183
+
184
+ bootstrapApplication(AppComponent, appConfig);
80
185
  ```
81
186
 
82
- Call it before the first element renders. Read [Configuration](#configuration)
83
- for why two of those three are functions — it is the difference between a
84
- session that keeps working and one that dies when the token expires.
187
+ Import order matters: the import registers the three tags as a side effect, so it
188
+ has to run before Angular renders a template containing one.
85
189
 
86
- ### 4. Drop in a tag
190
+ **2. Host the tag**
87
191
 
88
- ```html
89
- <aastrika-content-studio></aastrika-content-studio>
192
+ ```ts
193
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
194
+
195
+ @Component({
196
+ selector: 'app-training',
197
+ standalone: true,
198
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
199
+ template: `<aastrika-content-studio></aastrika-content-studio>`,
200
+ })
201
+ export class TrainingComponent {}
90
202
  ```
91
203
 
92
- That is the whole integration.
204
+ <details>
205
+ <summary><strong>Routing, styling, and why the schema matters</strong></summary>
93
206
 
94
- ## Framework notes
207
+ `CUSTOM_ELEMENTS_SCHEMA` switches off template checking for **every** unknown
208
+ tag in that component, not just ours — which is why the package ships tag
209
+ declarations. They give it back for these three, so a typo in
210
+ `aastrika-assessment` is still an error rather than a silently empty box.
95
211
 
96
- ### React
97
-
98
- ```jsx
99
- import '@aastrika/ai-elements';
212
+ **Little else to wire.** `apiBase`, the auth headers and the creator all come
213
+ from `configure()`, and each element manages its own state, so there is no
214
+ cleanup to write. Beyond that there are optional
215
+ [inputs and events](#inputs-and-events) — and `edit-mode`, which is worth
216
+ knowing about up front:
100
217
 
101
- <aastrika-content-studio />
218
+ ```html
219
+ <aastrika-content-studio edit-mode="on"></aastrika-content-studio>
102
220
  ```
103
221
 
104
- React 19 passes properties and custom events natively — no wrapper, no `ref`.
222
+ `edit-mode` turns on the segment editor for an uploaded video, and defaults to
223
+ `off`. Leave it off unless the host is **same-origin** with the service: the
224
+ editor loads per-segment thumbnails the service streams off local disk, and
225
+ unlike the finished video those have no presigned URL, so they cannot load from
226
+ another origin. The other two elements take no attributes.
105
227
 
106
- ### Angular
228
+ **Routing** works normally — put the tag in a lazily loaded route and the element
229
+ mounts on navigation, unmounts on leaving.
230
+
231
+ **Styling** goes through CSS custom properties. Angular's view encapsulation
232
+ rewrites a component-scoped rule into something that will not match the
233
+ element, so set the properties from a global stylesheet:
234
+
235
+ ```css
236
+ /* styles.css */
237
+ aastrika-content-studio { --aastrika-gold: #0F766E; }
238
+ ```
239
+
240
+ **A blank box** almost always means `configure()` ran after the template, or the
241
+ import was tree-shaken because nothing referenced it. Importing `configure` and
242
+ calling it, as above, prevents both.
107
243
 
108
- Add `CUSTOM_ELEMENTS_SCHEMA` to the component that uses the tag. The package
109
- ships tag declarations, so these three keep their template checking.
244
+ </details>
110
245
 
111
246
  ### Plain HTML
112
247
 
@@ -115,6 +250,8 @@ ships tag declarations, so these three keep their template checking.
115
250
  <aastrika-content-studio></aastrika-content-studio>
116
251
  ```
117
252
 
253
+ ---
254
+
118
255
  ## Configuration
119
256
 
120
257
  Three things, and nothing else.
@@ -124,6 +261,7 @@ Three things, and nothing else.
124
261
  | `apiBase` | Where the service is, no trailing slash. Empty string means same-origin. |
125
262
  | `getAuthHeaders` | Headers proving the caller may use the service — in practice a bearer token. |
126
263
  | `creator` | The username of whoever is signed into **your** application. |
264
+ | `onError` | Optional. Called whenever a request fails, so your monitoring hears about it. |
127
265
 
128
266
  **Both are functions, and both are called on every request.** Tokens expire: a
129
267
  string handed over at startup stops working mid-session and the package has no
@@ -137,6 +275,8 @@ gateway decides whether the call is allowed at all.
137
275
  The package authenticates nobody and has no login of its own. You are already
138
276
  signed in, and you know who that is better than we could.
139
277
 
278
+ ---
279
+
140
280
  ## Access
141
281
 
142
282
  Ask the Aastrika platform team for a Kong consumer with the groups each feature
@@ -154,11 +294,11 @@ features still work, but the revise step returns 403.
154
294
  > **`generate-video` is the only call that spends real money**, and it sits on a
155
295
  > deliberately low rate limit. Users see a cost estimate before triggering it.
156
296
 
297
+ ---
298
+
157
299
  ## Theming
158
300
 
159
- CSS custom properties are the entire styling surface. Everything renders inside
160
- a shadow root, so your CSS cannot reach in — and the package cannot reach out
161
- and break your layout.
301
+ CSS custom properties are the styling surface:
162
302
 
163
303
  ```css
164
304
  aastrika-content-studio {
@@ -168,14 +308,93 @@ aastrika-content-studio {
168
308
  }
169
309
  ```
170
310
 
171
- ## Events
311
+ **These render into your page, not a shadow root.** Angular's emulated view
312
+ encapsulation scopes the package's own CSS, so it will not leak out and break
313
+ your layout. It does not stop your CSS reaching in: a global rule on a common
314
+ class name — `.card`, `.btn`, `.field` — will hit these elements too. If your
315
+ stylesheet is broad, scope it away from the three tags.
316
+
317
+ ---
318
+
319
+ ## Knowing when something failed
320
+
321
+ The elements show their own error to the user. `onError` is the second copy, for
322
+ your monitoring:
323
+
324
+ ```js
325
+ configure({
326
+ apiBase: '...',
327
+ onError: ({ feature, message, status, code }) => {
328
+ Sentry.captureMessage(`aastrika/${feature}: ${message}`, { extra: { status, code } });
329
+ },
330
+ });
331
+ ```
332
+
333
+ | | |
334
+ |---|---|
335
+ | `feature` | `content-studio`, `assessment` or `reports` |
336
+ | `message` | The sentence shown to the user |
337
+ | `status` | HTTP status. **`0` means the request never left the browser** — usually CORS |
338
+ | `code` | The service's code, when it sent one, e.g. `RENDER_TIMEOUT` |
339
+
340
+ Without it, an element failing every call looks exactly like one nobody has
341
+ used yet. Throwing from inside it is safe — the exception is swallowed, so a
342
+ broken reporter cannot break the feature reporting to it.
343
+
344
+ ---
345
+
346
+ ## Inputs and events
347
+
348
+ Every element works with nothing bound. These are for hosts that want to open
349
+ the form already set up, or act on what the user produced.
350
+
351
+ ### Inputs
352
+
353
+ Attributes in HTML, properties in JS — Angular Elements maps one to the other.
354
+ All are **starting values** the user can still change.
355
+
356
+ | Element | Input | |
357
+ |---|---|---|
358
+ | `content-studio` | `default-language` | `hi`, `en`, `kn`, `te` |
359
+ | | `default-aspect-ratio` | `16:9` or `9:16` |
360
+ | | `default-depth` | How much detail the video goes into |
361
+ | | `default-voice` | A voice id from `/v1/studio/list-voices` |
362
+ | | `edit-mode` | `on` shows the segment editor. Same-origin hosts only |
363
+ | `assessment` | `default-language` | |
364
+ | | `default-question-count` | |
365
+ | | `default-difficulty` | `mixed`, `easy`, `medium`, `hard` |
366
+ | `reports` | `creator-filter` | Opens filtered to one creator |
367
+
368
+ ```html
369
+ <aastrika-content-studio default-language="kn" default-aspect-ratio="9:16">
370
+ </aastrika-content-studio>
371
+ ```
372
+
373
+ ### Events
374
+
375
+ | Element | Event | Fires when | `detail` |
376
+ |---|---|---|---|
377
+ | `content-studio` | `planReady` | A plan comes back. **Repeatable** — free, so this can fire several times | `jobId`, `title`, `sceneCount`, `estimatedUsd` |
378
+ | | `videoStarted` | The user commits to a render. **This is where money is spent** | `jobId` |
379
+ | | `videoReady` | The video is finished | `jobId`, `videoUrl` |
380
+ | `assessment` | `assessmentReady` | A validated question set comes back | `jobId`, `questionCount`, `language` |
381
+ | `reports` | `reportLoaded` | The figures are on screen | `videos`, `assessments`, `users`, `spendUsd` |
172
382
 
173
383
  ```js
174
- el.addEventListener('aastrika-job-started', (e) => save(e.detail.jobId));
175
- el.addEventListener('aastrika-video-ready', (e) => show(e.detail.videoUrl));
176
- el.addEventListener('aastrika-error', (e) => toast(e.detail.message));
384
+ el.addEventListener('videoReady', (e) => {
385
+ saveToOurRecords(e.detail.jobId, e.detail.videoUrl);
386
+ });
177
387
  ```
178
388
 
389
+ `videoUrl` plays without an `Authorization` header, so it can go straight into a
390
+ `<video src>` or be stored as-is. It is null on the local storage driver, where
391
+ there is nothing to presign.
392
+
393
+ **React** passes properties and listeners natively. **Angular** binds them like
394
+ any DOM event: `(videoReady)="onReady($event)"`.
395
+
396
+ ---
397
+
179
398
  ## When it does not work
180
399
 
181
400
  | Symptom | Cause |
@@ -188,7 +407,33 @@ el.addEventListener('aastrika-error', (e) => toast(e.detail.message));
188
407
  | Creator recorded as `unknown` | the gateway's CORS config is dropping `x-aastrika-creator` — tell us |
189
408
  | Blank box, no errors | the module did not load, so the tag is an unknown element |
190
409
 
410
+ ---
411
+
191
412
  ## Browser support
192
413
 
193
414
  Any browser with Custom Elements v1 and Shadow DOM — everything since 2018.
194
- Not server-rendered; the elements hydrate client-side.
415
+
416
+ ## Server rendering
417
+
418
+ **Import this package only in browser code.** It is not server-renderable, and
419
+ the failure is a crash rather than an empty box: `@angular/elements` declares a
420
+ class extending `HTMLElement` at module scope, so merely importing the package
421
+ where there is no DOM throws `ReferenceError: HTMLElement is not defined`.
422
+
423
+ Next.js:
424
+
425
+ ```jsx
426
+ import dynamic from 'next/dynamic';
427
+
428
+ const Studio = dynamic(
429
+ () => import('@aastrika/ai-elements').then(() => () => <aastrika-content-studio />),
430
+ { ssr: false },
431
+ );
432
+ ```
433
+
434
+ Angular Universal: keep the import inside a browser-only guard, or load it in
435
+ `ngAfterViewInit` rather than at module scope.
436
+
437
+ Nothing is lost by this. The elements have no server-rendered output to hydrate
438
+ — they fetch everything at runtime — so a client-only import renders exactly the
439
+ same page.
@@ -1,21 +1,157 @@
1
1
  /**
2
- * Tag declarations for consumers.
2
+ * The published type surface. This file IS the public API.
3
3
  *
4
- * An Angular host embedding these has to add CUSTOM_ELEMENTS_SCHEMA, which
5
- * switches off template checking for every unknown tag in that component — not
6
- * just ours. Declaring the tags here gives that back for these three, so a
7
- * typo in `aastrika-assessment` is still an error rather than a silently empty
8
- * box. React and Vue hosts get editor completion from the same declarations.
4
+ * It is hand-written rather than generated, because the bundle Angular emits is
5
+ * one file with no declarations and the three values a consumer touches are
6
+ * stable enough to state directly. Two rules follow from that:
7
+ *
8
+ * - Anything exported from src/elements/main.ts must be declared here, or a
9
+ * TypeScript consumer gets `any` for the one call the README mandates.
10
+ * - Anything declared here is a promise. Removing or renaming it is a
11
+ * breaking change; tests/lib/public-surface.test.ts fails on any drift.
12
+ *
13
+ * The tag declarations matter for a second reason. An Angular host embedding
14
+ * these adds CUSTOM_ELEMENTS_SCHEMA, which switches off template checking for
15
+ * every unknown tag in that component — not just ours. Declaring the tags gives
16
+ * it back for these three, so a typo in `aastrika-assessment` is still an error
17
+ * rather than a silently empty box. React and Vue hosts get completion from the
18
+ * same declarations.
19
+ */
20
+
21
+ /** What a request failure tells the host. Stable across features. */
22
+ export interface AastrikaError {
23
+ /** Which element the failure came from. */
24
+ feature: 'content-studio' | 'assessment' | 'reports';
25
+ /** The sentence already shown to the user. */
26
+ message: string;
27
+ /** HTTP status. 0 means the request never left the browser — usually CORS. */
28
+ status?: number;
29
+ /** The service's stable code, when it sent one, e.g. `RENDER_TIMEOUT`. */
30
+ code?: string;
31
+ }
32
+
33
+ /** Everything a host application tells this package. */
34
+ export interface AastrikaConfig {
35
+ /** Where the service is, no trailing slash. Empty string means same-origin. */
36
+ apiBase: string;
37
+ /** Headers proving the caller may use the service. Called before every request. */
38
+ getAuthHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
39
+ /** The username signed into YOUR application. Attribution, never verified. */
40
+ creator?: () => string | null;
41
+ /** Called whenever a request fails, so your monitoring hears about it. */
42
+ onError?: (error: AastrikaError) => void;
43
+ }
44
+
45
+ /**
46
+ * Point the package at a service. Call once, before or after the elements are
47
+ * on the page; calling again replaces the configuration.
48
+ */
49
+ export declare function configure(config: AastrikaConfig): void;
50
+
51
+ /**
52
+ * Resolves once the three tags are registered.
53
+ *
54
+ * Importing this module registers them as a side effect, so a host rarely needs
55
+ * this — it is here for code that must not run until the elements exist.
9
56
  */
57
+ export declare const ready: Promise<unknown>;
10
58
 
11
- interface AastrikaElement extends HTMLElement {}
59
+ /**
60
+ * Attributes are set in HTML (`default-language="kn"`), properties in JS
61
+ * (`el.defaultLanguage = 'kn'`), and events with addEventListener. Angular
62
+ * Elements maps one to the other, so a host uses whichever suits it.
63
+ *
64
+ * Every input is a STARTING value the user can still change, and every event
65
+ * is safe to ignore — an element with nothing bound behaves exactly as before.
66
+ */
67
+
68
+ /** `<aastrika-content-studio>` — a document becomes a narrated training video. */
69
+ interface AastrikaContentStudioElement extends HTMLElement {
70
+ /**
71
+ * `'on'` shows the segment editor for an uploaded video. Defaults to `'off'`.
72
+ *
73
+ * Off by default because the editor loads per-segment thumbnails the service
74
+ * streams off local disk, which — unlike the finished video — have no
75
+ * presigned URL and so cannot load from another origin. Turn it on only for a
76
+ * same-origin host.
77
+ */
78
+ editMode?: 'on' | 'off';
79
+ /** Language the form opens on, e.g. `'hi'`, `'en'`, `'kn'`, `'te'`. */
80
+ defaultLanguage?: string;
81
+ /** `'16:9'` or `'9:16'`. */
82
+ defaultAspectRatio?: string;
83
+ /** How much detail the video goes into, e.g. `'standard'`. */
84
+ defaultDepth?: string;
85
+ /** A voice id from `/v1/studio/list-voices`. */
86
+ defaultVoice?: string;
87
+
88
+ /** A plan came back. Free and repeatable, so this can fire more than once. */
89
+ addEventListener(
90
+ type: 'planReady',
91
+ listener: (e: CustomEvent<{ jobId: string; title: string; sceneCount: number; estimatedUsd: number | null }>) => void,
92
+ options?: boolean | AddEventListenerOptions,
93
+ ): void;
94
+ /** The user committed to a render. This is the point money is spent. */
95
+ addEventListener(type: 'videoStarted', listener: (e: CustomEvent<{ jobId: string }>) => void,
96
+ options?: boolean | AddEventListenerOptions,
97
+ ): void;
98
+ /** The video finished. `videoUrl` plays without an Authorization header. */
99
+ addEventListener(
100
+ type: 'videoReady',
101
+ listener: (e: CustomEvent<{ jobId: string; videoUrl: string }>) => void,
102
+ options?: boolean | AddEventListenerOptions,
103
+ ): void;
104
+ addEventListener(
105
+ type: string,
106
+ listener: EventListenerOrEventListenerObject,
107
+ options?: boolean | AddEventListenerOptions,
108
+ ): void;
109
+ }
110
+
111
+ /** `<aastrika-assessment>` — source material becomes a validated MCQ set. */
112
+ interface AastrikaAssessmentElement extends HTMLElement {
113
+ /** Language the form opens on. */
114
+ defaultLanguage?: string;
115
+ /** How many questions to ask for. */
116
+ defaultQuestionCount?: number;
117
+ /** `'mixed'`, `'easy'`, `'medium'` or `'hard'`. */
118
+ defaultDifficulty?: string;
119
+
120
+ /** A validated question set came back. */
121
+ addEventListener(
122
+ type: 'assessmentReady',
123
+ listener: (e: CustomEvent<{ jobId: string; questionCount: number; language: string }>) => void,
124
+ options?: boolean | AddEventListenerOptions,
125
+ ): void;
126
+ addEventListener(
127
+ type: string,
128
+ listener: EventListenerOrEventListenerObject,
129
+ options?: boolean | AddEventListenerOptions,
130
+ ): void;
131
+ }
132
+
133
+ /** `<aastrika-reports>` — usage and spend. Needs `contentAdmin` at the gateway. */
134
+ interface AastrikaReportsElement extends HTMLElement {
135
+ /** Opens filtered to one creator. The user can still clear it. */
136
+ creatorFilter?: string;
137
+
138
+ /** The figures are on screen. */
139
+ addEventListener(
140
+ type: 'reportLoaded',
141
+ listener: (e: CustomEvent<{ videos: number; assessments: number; users: number; spendUsd: number }>) => void,
142
+ options?: boolean | AddEventListenerOptions,
143
+ ): void;
144
+ addEventListener(
145
+ type: string,
146
+ listener: EventListenerOrEventListenerObject,
147
+ options?: boolean | AddEventListenerOptions,
148
+ ): void;
149
+ }
12
150
 
13
151
  declare global {
14
152
  interface HTMLElementTagNameMap {
15
- 'aastrika-content-studio': AastrikaElement;
16
- 'aastrika-assessment': AastrikaElement;
17
- 'aastrika-reports': AastrikaElement;
153
+ 'aastrika-content-studio': AastrikaContentStudioElement;
154
+ 'aastrika-assessment': AastrikaAssessmentElement;
155
+ 'aastrika-reports': AastrikaReportsElement;
18
156
  }
19
157
  }
20
-
21
- export {};