@mochabug/adapt-angular 1.0.1-rc.9

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 ADDED
@@ -0,0 +1,502 @@
1
+ # @mochabug/adapt-angular
2
+
3
+ Angular component for Adapt.
4
+
5
+ ```bash
6
+ npm install @mochabug/adapt-angular
7
+ ```
8
+
9
+ Requires Angular 19 or later.
10
+
11
+ ## Quickstart
12
+
13
+ ```typescript
14
+ import { Component } from '@angular/core';
15
+ import { AdaptAutomation } from '@mochabug/adapt-angular';
16
+
17
+ @Component({
18
+ selector: 'app-root',
19
+ standalone: true,
20
+ imports: [AdaptAutomation],
21
+ template: `
22
+ <mb-adapt-automation [automationId]="'auto-123'" style="height: 600px;" />
23
+ `,
24
+ })
25
+ export class AppComponent {}
26
+ ```
27
+
28
+ With authentication:
29
+
30
+ ```html
31
+ <mb-adapt-automation [automationId]="'auto-123'" [authToken]="'your-token'" style="height: 600px;" />
32
+ ```
33
+
34
+ With proof-of-work challenge:
35
+
36
+ ```html
37
+ <mb-adapt-automation [automationId]="'auto-123'" [requiresChallenge]="true" style="height: 600px;" />
38
+ ```
39
+
40
+ ## SSR (Angular Universal / SSR)
41
+
42
+ Keep auth token on server:
43
+
44
+ ```typescript
45
+ import { Component, OnInit } from '@angular/core';
46
+ import { AdaptAutomation } from '@mochabug/adapt-angular';
47
+ import { startSession } from '@mochabug/adapt-core';
48
+
49
+ @Component({
50
+ selector: 'app-root',
51
+ standalone: true,
52
+ imports: [AdaptAutomation],
53
+ template: `
54
+ @if (sessionToken) {
55
+ <mb-adapt-automation [automationId]="'auto-123'" [sessionToken]="sessionToken" style="height: 600px;" />
56
+ }
57
+ `,
58
+ })
59
+ export class AppComponent implements OnInit {
60
+ sessionToken?: string;
61
+
62
+ async ngOnInit() {
63
+ const authToken = await getAuthTokenFromBackend();
64
+ const { token } = await startSession({ id: 'auto-123' }, authToken);
65
+ this.sessionToken = token;
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Session inheritance
71
+
72
+ ```html
73
+ <!-- direct -->
74
+ <mb-adapt-automation [automationId]="'auto-123'" [inheritToken]="'token-from-parent'" />
75
+
76
+ <!-- from URL hash: example.com#mb_session=xxx -->
77
+ <mb-adapt-automation [automationId]="'auto-123'" [inheritFrom]="{ hash: 'mb_session' }" />
78
+ ```
79
+
80
+ ## Fork display
81
+
82
+ ```html
83
+ <!-- side-by-side (default) -->
84
+ <mb-adapt-automation [automationId]="'auto-123'" [forkDisplay]="{ mode: 'side-by-side', split: 60 }" />
85
+
86
+ <!-- dialog -->
87
+ <mb-adapt-automation [automationId]="'auto-123'" [forkDisplay]="{ mode: 'dialog' }" />
88
+ ```
89
+
90
+ ## Events
91
+
92
+ ```html
93
+ <mb-adapt-automation
94
+ [automationId]="'auto-123'"
95
+ (session)="onSession($event)"
96
+ (adaptOutput)="onOutput($event)"
97
+ (forkActive)="onForkActive($event)"
98
+ />
99
+ ```
100
+
101
+ ```typescript
102
+ onSession(event: { status: StatusJson; fork?: string }) {
103
+ console.log(event.status, event.fork);
104
+ }
105
+
106
+ onOutput(output: Output) {
107
+ console.log(output);
108
+ }
109
+
110
+ onForkActive(active: boolean) {
111
+ console.log(active);
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 2. `AdaptCap`
118
+
119
+ Standalone proof-of-work challenge widget. Use when you manage the automation client yourself.
120
+
121
+ ```typescript
122
+ import { Component } from '@angular/core';
123
+ import { AdaptCap, createConnectClient } from '@mochabug/adapt-angular/cap';
124
+
125
+ @Component({
126
+ selector: 'app-challenge',
127
+ standalone: true,
128
+ imports: [AdaptCap],
129
+ template: `
130
+ <mb-adapt-cap
131
+ [automationId]="'YOUR_ID'"
132
+ [client]="client"
133
+ (solve)="onSolve($event)"
134
+ (error)="onError($event)"
135
+ />
136
+ `,
137
+ })
138
+ export class ChallengeComponent {
139
+ client = createConnectClient({ id: 'YOUR_ID' });
140
+
141
+ onSolve(event: { token: string; expires: Date }) {
142
+ console.log('Solved:', event.token, event.expires);
143
+ }
144
+
145
+ onError(error: Error) {
146
+ console.error(error);
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Props
152
+
153
+ | Input | Type |
154
+ |-------|------|
155
+ | `automationId` | `string` (required) |
156
+ | `client` | `AutomationClient` (required) |
157
+ | `workerCount` | `number` |
158
+ | `i18n` | `CapWidgetI18n` |
159
+ | `darkMode` | `boolean` |
160
+
161
+ ### Events
162
+
163
+ | Output | Payload |
164
+ |--------|---------|
165
+ | `solve` | `{ token: string; expires: Date }` |
166
+ | `error` | `Error` |
167
+
168
+ ### Headless (no UI)
169
+
170
+ Use the lower-level API to create and redeem challenges yourself:
171
+
172
+ ```ts
173
+ import { createChallenge, redeemChallenge, createConnectClient } from '@mochabug/adapt-angular/cap';
174
+
175
+ const client = createConnectClient({ id: 'YOUR_ID' });
176
+ const challenge = await createChallenge(client);
177
+ // ... solve with Cap.js or your own solver ...
178
+ const redeemed = await redeemChallenge(client, solutions);
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Styling
184
+
185
+ All visuals are controlled via CSS custom properties on `.mb-adapt`. The `[darkMode]="true"` input automatically switches to dark defaults, but you can override any variable for either mode.
186
+
187
+ ### Where to put the CSS
188
+
189
+ Add a CSS file and import it in your `angular.json` styles array:
190
+
191
+ ```json
192
+ {
193
+ "styles": ["src/styles.css", "src/adapt-theme.css"]
194
+ }
195
+ ```
196
+
197
+ Or import a global CSS file in your root `styles.css`:
198
+
199
+ ```css
200
+ @import './adapt-theme.css';
201
+ ```
202
+
203
+ ### Matching your site's toolbar
204
+
205
+ Map your existing design tokens to the panel toolbar. These six variables control almost everything you see in the tab bar:
206
+
207
+ ```css
208
+ /* adapt-theme.css */
209
+
210
+ /* Light mode — derive from your site's surface/text colors */
211
+ .mb-adapt {
212
+ --mb-adapt-fork-bg: #ffffff; /* panel content background */
213
+ --mb-adapt-fork-tab-bg: #f5f5f5; /* inactive tab / toolbar background */
214
+ --mb-adapt-fork-tab-active-bg: #ffffff; /* active tab background */
215
+ --mb-adapt-fork-tab-color: #1a1a1a; /* active tab text */
216
+ --mb-adapt-fork-tab-inactive-color: #888; /* inactive tab text */
217
+ --mb-adapt-fork-separator: #e0e0e0; /* borders between tabs & panels */
218
+ }
219
+
220
+ /* Dark mode — active when [darkMode]="true" */
221
+ .mb-adapt[dark-mode] {
222
+ --mb-adapt-fork-bg: #1e1e1e;
223
+ --mb-adapt-fork-tab-bg: #2a2a2a;
224
+ --mb-adapt-fork-tab-active-bg: #1e1e1e;
225
+ --mb-adapt-fork-tab-color: #e0e0e0;
226
+ --mb-adapt-fork-tab-inactive-color: #777;
227
+ --mb-adapt-fork-separator: #3a3a3a;
228
+ }
229
+ ```
230
+
231
+ **Typical mapping from your site's design system:**
232
+
233
+ | Your site has | Maps to |
234
+ |---|---|
235
+ | Surface / card background | `--mb-adapt-fork-bg` |
236
+ | Secondary / muted background | `--mb-adapt-fork-tab-bg` |
237
+ | Primary text color | `--mb-adapt-fork-tab-color` |
238
+ | Muted / secondary text | `--mb-adapt-fork-tab-inactive-color` |
239
+ | Border / divider color | `--mb-adapt-fork-separator` |
240
+ | Accent color | `--mb-adapt-separator-active` (resize handle highlight) |
241
+ | Interactive hover tint | `--mb-adapt-button-hover-bg` |
242
+
243
+ ### Custom theme (light & dark)
244
+
245
+ A more complete example mapping a full brand palette — primary, secondary, and accent colors — to Adapt's CSS variables for both light and dark modes. Add this as a global CSS file via `angular.json` styles array or import it in your root stylesheet:
246
+
247
+ ```css
248
+ /* adapt-brand-theme.css */
249
+
250
+ /*
251
+ * Example: brand theme using primary / secondary / accent colors.
252
+ * Maps your design-system tokens to Adapt's CSS variables for both modes.
253
+ */
254
+
255
+ /* ── Light mode ────────────────────────────────── */
256
+ .mb-adapt {
257
+ /* Brand palette */
258
+ --mb-adapt-fork-bg: #ffffff; /* surface */
259
+ --mb-adapt-fork-tab-bg: #f0f4ff; /* primary-50 */
260
+ --mb-adapt-fork-tab-active-bg: #ffffff; /* surface */
261
+ --mb-adapt-fork-tab-color: #1e293b; /* on-surface */
262
+ --mb-adapt-fork-tab-inactive-color: #64748b; /* on-surface-muted */
263
+ --mb-adapt-fork-separator: #cbd5e1; /* outline */
264
+
265
+ /* Accent — resize handle highlight */
266
+ --mb-adapt-separator-active: rgba(79, 70, 229, 0.5); /* primary */
267
+
268
+ /* Cap widget */
269
+ --mb-adapt-cap-background: #ffffff;
270
+ --mb-adapt-cap-border-color: #e2e8f0;
271
+ --mb-adapt-cap-color: #1e293b;
272
+ --mb-adapt-cap-spinner-color: #4f46e5; /* primary */
273
+
274
+ /* Status cards */
275
+ --mb-adapt-status-card-bg: #ffffff;
276
+ --mb-adapt-status-card-border: #e2e8f0;
277
+ --mb-adapt-status-text: #334155;
278
+ }
279
+
280
+ /* ── Dark mode ─────────────────────────────────── */
281
+ .mb-adapt[dark-mode] {
282
+ --mb-adapt-fork-bg: #0f172a; /* surface-dark */
283
+ --mb-adapt-fork-tab-bg: #1e293b; /* primary-900 */
284
+ --mb-adapt-fork-tab-active-bg: #0f172a; /* surface-dark */
285
+ --mb-adapt-fork-tab-color: #f1f5f9; /* on-surface-dark */
286
+ --mb-adapt-fork-tab-inactive-color: #94a3b8; /* muted-dark */
287
+ --mb-adapt-fork-separator: #334155; /* outline-dark */
288
+
289
+ --mb-adapt-separator-active: rgba(129, 140, 248, 0.6); /* primary-light */
290
+
291
+ --mb-adapt-cap-background: #1e293b;
292
+ --mb-adapt-cap-border-color: #334155;
293
+ --mb-adapt-cap-color: #f1f5f9;
294
+ --mb-adapt-cap-spinner-color: #818cf8; /* primary-light */
295
+
296
+ --mb-adapt-status-card-bg: #1e293b;
297
+ --mb-adapt-status-card-border: #334155;
298
+ --mb-adapt-status-text: #e2e8f0;
299
+ }
300
+ ```
301
+
302
+ ### Font
303
+
304
+ ```css
305
+ .mb-adapt {
306
+ --mb-adapt-font: "Inter", sans-serif;
307
+ }
308
+ ```
309
+
310
+ ### Elevation and borders
311
+
312
+ ```css
313
+ /* Subtle, modern elevation */
314
+ .mb-adapt {
315
+ --mb-adapt-floating-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
316
+ --mb-adapt-floating-border: 1px solid rgba(0, 0, 0, 0.06);
317
+ --mb-adapt-floating-radius: 12px;
318
+ --mb-adapt-floating-backdrop: none;
319
+
320
+ --mb-adapt-status-card-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
321
+ --mb-adapt-drag-ghost-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
322
+ --mb-adapt-border-radius: 8px; /* iframe border radius */
323
+ }
324
+
325
+ /* Dark mode */
326
+ .mb-adapt[dark-mode] {
327
+ --mb-adapt-floating-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
328
+ --mb-adapt-floating-border: 1px solid rgba(255, 255, 255, 0.08);
329
+ --mb-adapt-status-card-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
330
+ --mb-adapt-drag-ghost-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
331
+ }
332
+ ```
333
+
334
+ **Frosted glass effect:**
335
+
336
+ ```css
337
+ .mb-adapt {
338
+ --mb-adapt-fork-bg: rgba(255, 255, 255, 0.7);
339
+ --mb-adapt-fork-tab-bg: rgba(245, 245, 245, 0.5);
340
+ --mb-adapt-floating-backdrop: blur(16px) saturate(180%);
341
+ --mb-adapt-floating-border: 1px solid rgba(255, 255, 255, 0.2);
342
+ --mb-adapt-floating-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
343
+ }
344
+ ```
345
+
346
+ **Flat / borderless:**
347
+
348
+ ```css
349
+ .mb-adapt {
350
+ --mb-adapt-floating-shadow: none;
351
+ --mb-adapt-floating-border: 1px solid var(--mb-adapt-fork-separator);
352
+ --mb-adapt-floating-radius: 4px;
353
+ --mb-adapt-status-card-shadow: none;
354
+ --mb-adapt-drag-ghost-shadow: none;
355
+ }
356
+ ```
357
+
358
+ ### Using the `classNames` input
359
+
360
+ Override internal element classes for deeper customization:
361
+
362
+ ```html
363
+ <mb-adapt-automation
364
+ [automationId]="'auto-123'"
365
+ [classNames]="{
366
+ root: 'my-adapt-root',
367
+ iframe: 'my-adapt-iframe',
368
+ statusMessage: 'my-status-overlay',
369
+ statusCard: 'my-status-card'
370
+ }"
371
+ />
372
+ ```
373
+
374
+ ### Using the `styles` input
375
+
376
+ Apply inline styles to the internal root element (useful for sizing):
377
+
378
+ ```html
379
+ <mb-adapt-automation
380
+ [automationId]="'auto-123'"
381
+ [styles]="{ height: '600px', maxWidth: '1200px', margin: '0 auto' }"
382
+ />
383
+ ```
384
+
385
+ > **Note:** `[styles]` targets the internal `.mb-adapt` root. Use Angular's `style` attribute or `[ngStyle]` for styles on the outer `<mb-adapt-automation>` host element.
386
+
387
+ <details>
388
+ <summary>Full CSS variable reference</summary>
389
+
390
+ ### General
391
+
392
+ | Variable | Light default | Dark default | Description |
393
+ |---|---|---|---|
394
+ | `--mb-adapt-bg` | `transparent` | | Root & group backgrounds |
395
+ | `--mb-adapt-font` | `system-ui, -apple-system, sans-serif` | | All panel UI text |
396
+ | `--mb-adapt-button-hover-bg` | `rgba(128,128,128,0.2)` | `rgba(128,128,128,0.3)` | Close/popout/action button hover |
397
+ | `--mb-adapt-separator-active` | `rgba(59,130,246,0.5)` | `rgba(99,130,246,0.6)` | Resize handle hover/active |
398
+ | `--mb-adapt-border-radius` | `8px` | | Iframe border radius |
399
+
400
+ ### Toolbar and tabs
401
+
402
+ | Variable | Light default | Dark default | Description |
403
+ |---|---|---|---|
404
+ | `--mb-adapt-fork-bg` | `#ffffff` | `#1e1e1e` | Panel content background |
405
+ | `--mb-adapt-fork-tab-bg` | `#f3f3f3` | `#252526` | Toolbar / inactive tab bg |
406
+ | `--mb-adapt-fork-tab-active-bg` | `#ffffff` | `#1e1e1e` | Active tab background |
407
+ | `--mb-adapt-fork-tab-color` | `rgb(51,51,51)` | `#ffffff` | Active tab text |
408
+ | `--mb-adapt-fork-tab-inactive-color` | `rgba(51,51,51,0.7)` | `#969696` | Inactive tab text |
409
+ | `--mb-adapt-fork-separator` | `rgba(128,128,128,0.35)` | `rgb(68,68,68)` | Tab/panel borders |
410
+
411
+ ### Floating panels (elevation)
412
+
413
+ | Variable | Light default | Dark default | Description |
414
+ |---|---|---|---|
415
+ | `--mb-adapt-floating-shadow` | `0 25px 50px -12px rgba(0,0,0,0.25), 0 12px 24px -8px rgba(0,0,0,0.15)` | `… rgba(0,0,0,0.5), … rgba(0,0,0,0.3)` | Overlay box-shadow |
416
+ | `--mb-adapt-floating-border` | `none` | `1px solid rgba(255,255,255,0.06)` | Overlay border |
417
+ | `--mb-adapt-floating-backdrop` | `none` | | Overlay backdrop-filter |
418
+ | `--mb-adapt-floating-radius` | `8px` | | Overlay border-radius |
419
+ | `--mb-adapt-status-card-shadow` | `0 4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04)` | `… rgba(0,0,0,0.25), … rgba(0,0,0,0.15)` | Status card box-shadow |
420
+ | `--mb-adapt-drag-ghost-shadow` | `0 4px 12px rgba(0,0,0,0.15)` | `0 4px 12px rgba(0,0,0,0.35)` | Drag ghost box-shadow |
421
+
422
+ ### Drop targets
423
+
424
+ | Variable | Light default | Dark default |
425
+ |---|---|---|
426
+ | `--mb-adapt-drop-header-bg` | `rgba(99,102,241,0.18)` | `rgba(129,140,248,0.22)` |
427
+ | `--mb-adapt-drop-center-bg` | `rgba(99,102,241,0.12)` | `rgba(129,140,248,0.15)` |
428
+ | `--mb-adapt-drop-split-bg` | `rgba(99,102,241,0.14)` | `rgba(129,140,248,0.18)` |
429
+ | `--mb-adapt-drop-border` | `rgba(99,102,241,0.55)` | `rgba(129,140,248,0.6)` |
430
+
431
+ ### Status cards
432
+
433
+ | Variable | Light default | Dark default |
434
+ |---|---|---|
435
+ | `--mb-adapt-status-card-bg` | `#ffffff` | `#1e293b` |
436
+ | `--mb-adapt-status-card-border` | `#e5e7eb` | `#334155` |
437
+ | `--mb-adapt-status-icon-bg` | `#fef2f2` | `#351c1c` |
438
+ | `--mb-adapt-status-text` | `#374151` | `#e2e8f0` |
439
+
440
+ ### Cap widget
441
+
442
+ | Variable | Light default | Dark default |
443
+ |---|---|---|
444
+ | `--mb-adapt-cap-background` | `#ffffff` | `#1e293b` |
445
+ | `--mb-adapt-cap-border-color` | `#e2e8f0` | `#334155` |
446
+ | `--mb-adapt-cap-border-radius` | `16px` | |
447
+ | `--mb-adapt-cap-height` | `72px` | |
448
+ | `--mb-adapt-cap-width` | `380px` | |
449
+ | `--mb-adapt-cap-padding` | `20px 28px` | |
450
+ | `--mb-adapt-cap-gap` | `20px` | |
451
+ | `--mb-adapt-cap-color` | `#1e293b` | `#f1f5f9` |
452
+ | `--mb-adapt-cap-checkbox-size` | `36px` | |
453
+ | `--mb-adapt-cap-checkbox-border` | `2px solid #cbd5e1` | `2px solid #475569` |
454
+ | `--mb-adapt-cap-checkbox-radius` | `10px` | |
455
+ | `--mb-adapt-cap-checkbox-background` | `#f8fafc` | `#0f172a` |
456
+ | `--mb-adapt-cap-spinner-color` | `#6366f1` | `#818cf8` |
457
+ | `--mb-adapt-cap-spinner-bg` | `#e2e8f0` | `#334155` |
458
+ | `--mb-adapt-cap-spinner-thickness` | `3px` | |
459
+ | `--mb-adapt-cap-font` | `inherit` | |
460
+
461
+ </details>
462
+
463
+ ---
464
+
465
+ ## Props
466
+
467
+ | Input | Type |
468
+ |-------|------|
469
+ | `automationId` | `string` (required) |
470
+ | `sessionToken` | `string` |
471
+ | `authToken` | `string` |
472
+ | `transmitter` | `string` |
473
+ | `signals` | `{ [key: string]: SignalValue }` |
474
+ | `challengeToken` | `string` |
475
+ | `requiresChallenge` | `boolean` |
476
+ | `capWidgetOptions` | `{ workerCount?: number; i18n?: CapWidgetI18n }` |
477
+ | `inheritToken` | `string` |
478
+ | `inheritFrom` | `{ hash: string } \| { param: string }` |
479
+ | `forkDisplay` | `{ mode: 'side-by-side', split?: number } \| { mode: 'dialog' }` |
480
+ | `darkMode` | `boolean` |
481
+ | `autoResizing` | `boolean` |
482
+ | `allowFloating` | `boolean` — hide pop-out buttons and block user-initiated floating (default `true`) |
483
+ | `allowDocking` | `boolean` — hide dock buttons and block user-initiated docking (default `true`) |
484
+ | `allowDialogDocking` | `boolean` — allow tab splits inside floating dialog overlays (default `true`) |
485
+ | `floatingAutoResize` | `boolean` — floating overlays auto-resize from iframe content (default `false`) |
486
+ | `confirmOnClose` | `boolean` — show confirmation dialog before leaving page (default `false`) |
487
+ | `persist` | `boolean \| PersistOptions` |
488
+ | `text` | `StatusText` |
489
+ | `classNames` | `{ root?: string; iframe?: string; statusMessage?: string; statusCard?: string }` |
490
+ | `styles` | `Partial<CSSStyleDeclaration>` |
491
+
492
+ ## Events
493
+
494
+ | Output | Payload |
495
+ |--------|---------|
496
+ | `session` | `{ status: StatusJson; fork?: string }` |
497
+ | `adaptOutput` | `Output` |
498
+ | `forkActive` | `boolean` |
499
+
500
+ ## License
501
+
502
+ ISC (c) mochabug AB
@@ -0,0 +1,60 @@
1
+ import { AfterViewInit, EventEmitter, NgZone, OnChanges, OnDestroy } from "@angular/core";
2
+ import { type AdaptWebClientOptions, type ForkDisplay, type Output as AdaptOutput, type PersistOptions, type SignalValue, type StatusJson, type StatusText } from "@mochabug/adapt-web";
3
+ import "@mochabug/adapt-web/cap";
4
+ import * as i0 from "@angular/core";
5
+ /**
6
+ * Angular component for embedding Adapt automations.
7
+ * Renders the `<adapt-automation>` custom element internally and syncs
8
+ * Angular inputs/outputs to the element's attributes and properties.
9
+ */
10
+ export declare class AdaptAutomation implements AfterViewInit, OnChanges, OnDestroy {
11
+ private zone;
12
+ private elRef;
13
+ automationId: string;
14
+ sessionToken?: string;
15
+ authToken?: string;
16
+ transmitter?: string;
17
+ signals?: Record<string, SignalValue>;
18
+ challengeToken?: string;
19
+ requiresChallenge?: boolean;
20
+ capWidgetOptions?: AdaptWebClientOptions["capWidgetOptions"];
21
+ inheritToken?: string;
22
+ inheritFrom?: {
23
+ hash: string;
24
+ } | {
25
+ param: string;
26
+ };
27
+ forkDisplay?: ForkDisplay;
28
+ darkMode?: boolean;
29
+ autoResizing?: boolean;
30
+ allowFloating?: boolean;
31
+ allowDocking?: boolean;
32
+ allowDialogDocking?: boolean;
33
+ allowMinimize?: boolean;
34
+ allowMaximize?: boolean;
35
+ floatingAutoResize?: boolean;
36
+ confirmOnClose?: boolean;
37
+ persist?: boolean | PersistOptions;
38
+ text?: StatusText;
39
+ classNames?: AdaptWebClientOptions["classNames"];
40
+ styles?: Partial<CSSStyleDeclaration>;
41
+ session: EventEmitter<{
42
+ status: StatusJson;
43
+ fork?: string;
44
+ }>;
45
+ adaptOutput: EventEmitter<AdaptOutput>;
46
+ forkActive: EventEmitter<boolean>;
47
+ private initialized;
48
+ constructor(zone: NgZone);
49
+ ngAfterViewInit(): void;
50
+ ngOnChanges(): void;
51
+ ngOnDestroy(): void;
52
+ /** End current session and start a new one. */
53
+ newSession(): Promise<void>;
54
+ private sync;
55
+ private setAttr;
56
+ private setBoolAttr;
57
+ private setFalseAttr;
58
+ static ɵfac: i0.ɵɵFactoryDeclaration<AdaptAutomation, never>;
59
+ static ɵcmp: i0.ɵɵComponentDeclaration<AdaptAutomation, "mb-adapt-automation", never, { "automationId": { "alias": "automationId"; "required": true; }; "sessionToken": { "alias": "sessionToken"; "required": false; }; "authToken": { "alias": "authToken"; "required": false; }; "transmitter": { "alias": "transmitter"; "required": false; }; "signals": { "alias": "signals"; "required": false; }; "challengeToken": { "alias": "challengeToken"; "required": false; }; "requiresChallenge": { "alias": "requiresChallenge"; "required": false; }; "capWidgetOptions": { "alias": "capWidgetOptions"; "required": false; }; "inheritToken": { "alias": "inheritToken"; "required": false; }; "inheritFrom": { "alias": "inheritFrom"; "required": false; }; "forkDisplay": { "alias": "forkDisplay"; "required": false; }; "darkMode": { "alias": "darkMode"; "required": false; }; "autoResizing": { "alias": "autoResizing"; "required": false; }; "allowFloating": { "alias": "allowFloating"; "required": false; }; "allowDocking": { "alias": "allowDocking"; "required": false; }; "allowDialogDocking": { "alias": "allowDialogDocking"; "required": false; }; "allowMinimize": { "alias": "allowMinimize"; "required": false; }; "allowMaximize": { "alias": "allowMaximize"; "required": false; }; "floatingAutoResize": { "alias": "floatingAutoResize"; "required": false; }; "confirmOnClose": { "alias": "confirmOnClose"; "required": false; }; "persist": { "alias": "persist"; "required": false; }; "text": { "alias": "text"; "required": false; }; "classNames": { "alias": "classNames"; "required": false; }; "styles": { "alias": "styles"; "required": false; }; }, { "session": "session"; "adaptOutput": "adaptOutput"; "forkActive": "forkActive"; }, never, never, true, never>;
60
+ }
@@ -0,0 +1,177 @@
1
+ import { Component, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, Input, Output, ViewChild, } from "@angular/core";
2
+ import { AdaptAutomationElement, } from "@mochabug/adapt-web";
3
+ import "@mochabug/adapt-web/cap";
4
+ import * as i0 from "@angular/core";
5
+ // Ensure custom element is registered
6
+ void AdaptAutomationElement;
7
+ /**
8
+ * Angular component for embedding Adapt automations.
9
+ * Renders the `<adapt-automation>` custom element internally and syncs
10
+ * Angular inputs/outputs to the element's attributes and properties.
11
+ */
12
+ export class AdaptAutomation {
13
+ constructor(zone) {
14
+ this.zone = zone;
15
+ this.session = new EventEmitter();
16
+ this.adaptOutput = new EventEmitter();
17
+ this.forkActive = new EventEmitter();
18
+ this.initialized = false;
19
+ }
20
+ ngAfterViewInit() {
21
+ this.sync();
22
+ this.initialized = true;
23
+ }
24
+ ngOnChanges() {
25
+ if (this.initialized) {
26
+ this.sync();
27
+ }
28
+ }
29
+ ngOnDestroy() {
30
+ // Custom element handles cleanup via disconnectedCallback
31
+ }
32
+ /** End current session and start a new one. */
33
+ async newSession() {
34
+ await this.elRef.nativeElement.newSession();
35
+ }
36
+ sync() {
37
+ const el = this.elRef.nativeElement;
38
+ // String attributes
39
+ this.setAttr(el, "automation-id", this.automationId);
40
+ this.setAttr(el, "session-token", this.sessionToken);
41
+ this.setAttr(el, "auth-token", this.authToken);
42
+ this.setAttr(el, "transmitter", this.transmitter);
43
+ this.setAttr(el, "challenge-token", this.challengeToken);
44
+ this.setAttr(el, "inherit-token", this.inheritToken);
45
+ // Boolean presence attributes
46
+ this.setBoolAttr(el, "requires-challenge", this.requiresChallenge);
47
+ this.setBoolAttr(el, "dark-mode", this.darkMode);
48
+ this.setBoolAttr(el, "auto-resizing", this.autoResizing);
49
+ this.setBoolAttr(el, "floating-auto-resize", this.floatingAutoResize);
50
+ this.setBoolAttr(el, "confirm-on-close", this.confirmOnClose);
51
+ this.setBoolAttr(el, "persist", !!this.persist);
52
+ // Boolean "false" attributes (default true, set "false" to disable)
53
+ this.setFalseAttr(el, "allow-floating", this.allowFloating);
54
+ this.setFalseAttr(el, "allow-docking", this.allowDocking);
55
+ this.setFalseAttr(el, "allow-dialog-docking", this.allowDialogDocking);
56
+ this.setFalseAttr(el, "allow-minimize", this.allowMinimize);
57
+ this.setFalseAttr(el, "allow-maximize", this.allowMaximize);
58
+ // Fork display attributes
59
+ if (this.forkDisplay) {
60
+ el.setAttribute("fork-display-mode", this.forkDisplay.mode);
61
+ if (this.forkDisplay.mode === "side-by-side" &&
62
+ this.forkDisplay.split !== undefined) {
63
+ el.setAttribute("side-by-side-split", String(this.forkDisplay.split));
64
+ }
65
+ else {
66
+ el.removeAttribute("side-by-side-split");
67
+ }
68
+ }
69
+ // Non-serializable properties
70
+ el.signals = this.signals;
71
+ el.capWidgetOptions = this.capWidgetOptions;
72
+ el.inheritFrom = this.inheritFrom;
73
+ el.classNames = this.classNames;
74
+ el.styles = this.styles;
75
+ el.persistOptions =
76
+ typeof this.persist === "object" ? this.persist : undefined;
77
+ el.text = this.text;
78
+ // Callbacks — run inside NgZone for change detection
79
+ el.onSessionCallback = (status, fork) => {
80
+ this.zone.run(() => this.session.emit({ status, fork }));
81
+ };
82
+ el.onOutputCallback = (output) => {
83
+ this.zone.run(() => this.adaptOutput.emit(output));
84
+ };
85
+ el.onForkActiveCallback = (active) => {
86
+ this.zone.run(() => this.forkActive.emit(active));
87
+ };
88
+ }
89
+ setAttr(el, name, value) {
90
+ if (value != null) {
91
+ el.setAttribute(name, value);
92
+ }
93
+ else {
94
+ el.removeAttribute(name);
95
+ }
96
+ }
97
+ setBoolAttr(el, name, value) {
98
+ if (value) {
99
+ el.setAttribute(name, "");
100
+ }
101
+ else {
102
+ el.removeAttribute(name);
103
+ }
104
+ }
105
+ setFalseAttr(el, name, value) {
106
+ if (value === false) {
107
+ el.setAttribute(name, "false");
108
+ }
109
+ else {
110
+ el.removeAttribute(name);
111
+ }
112
+ }
113
+ }
114
+ AdaptAutomation.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AdaptAutomation, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
115
+ AdaptAutomation.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.4", type: AdaptAutomation, isStandalone: true, selector: "mb-adapt-automation", inputs: { automationId: "automationId", sessionToken: "sessionToken", authToken: "authToken", transmitter: "transmitter", signals: "signals", challengeToken: "challengeToken", requiresChallenge: "requiresChallenge", capWidgetOptions: "capWidgetOptions", inheritToken: "inheritToken", inheritFrom: "inheritFrom", forkDisplay: "forkDisplay", darkMode: "darkMode", autoResizing: "autoResizing", allowFloating: "allowFloating", allowDocking: "allowDocking", allowDialogDocking: "allowDialogDocking", allowMinimize: "allowMinimize", allowMaximize: "allowMaximize", floatingAutoResize: "floatingAutoResize", confirmOnClose: "confirmOnClose", persist: "persist", text: "text", classNames: "classNames", styles: "styles" }, outputs: { session: "session", adaptOutput: "adaptOutput", forkActive: "forkActive" }, viewQueries: [{ propertyName: "elRef", first: true, predicate: ["el"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `<adapt-automation #el></adapt-automation>`, isInline: true, styles: [":host { display: block; }", "adapt-automation { display: block; width: 100%; height: 100%; }"] });
116
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AdaptAutomation, decorators: [{
117
+ type: Component,
118
+ args: [{ selector: "mb-adapt-automation", standalone: true, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<adapt-automation #el></adapt-automation>`, styles: [":host { display: block; }", "adapt-automation { display: block; width: 100%; height: 100%; }"] }]
119
+ }], ctorParameters: () => [{ type: i0.NgZone }], propDecorators: { elRef: [{
120
+ type: ViewChild,
121
+ args: ["el", { static: true }]
122
+ }], automationId: [{
123
+ type: Input,
124
+ args: [{ required: true }]
125
+ }], sessionToken: [{
126
+ type: Input
127
+ }], authToken: [{
128
+ type: Input
129
+ }], transmitter: [{
130
+ type: Input
131
+ }], signals: [{
132
+ type: Input
133
+ }], challengeToken: [{
134
+ type: Input
135
+ }], requiresChallenge: [{
136
+ type: Input
137
+ }], capWidgetOptions: [{
138
+ type: Input
139
+ }], inheritToken: [{
140
+ type: Input
141
+ }], inheritFrom: [{
142
+ type: Input
143
+ }], forkDisplay: [{
144
+ type: Input
145
+ }], darkMode: [{
146
+ type: Input
147
+ }], autoResizing: [{
148
+ type: Input
149
+ }], allowFloating: [{
150
+ type: Input
151
+ }], allowDocking: [{
152
+ type: Input
153
+ }], allowDialogDocking: [{
154
+ type: Input
155
+ }], allowMinimize: [{
156
+ type: Input
157
+ }], allowMaximize: [{
158
+ type: Input
159
+ }], floatingAutoResize: [{
160
+ type: Input
161
+ }], confirmOnClose: [{
162
+ type: Input
163
+ }], persist: [{
164
+ type: Input
165
+ }], text: [{
166
+ type: Input
167
+ }], classNames: [{
168
+ type: Input
169
+ }], styles: [{
170
+ type: Input
171
+ }], session: [{
172
+ type: Output
173
+ }], adaptOutput: [{
174
+ type: Output
175
+ }], forkActive: [{
176
+ type: Output
177
+ }] } });
@@ -0,0 +1,31 @@
1
+ import { AfterViewInit, EventEmitter, NgZone, OnChanges, OnDestroy } from "@angular/core";
2
+ import type { AutomationClient } from "@mochabug/adapt-core";
3
+ import { type CapWidgetI18n } from "@mochabug/adapt-web/cap";
4
+ import * as i0 from "@angular/core";
5
+ /**
6
+ * Angular component for Cap.js proof-of-work challenges.
7
+ * Renders the `<adapt-cap>` custom element internally and syncs
8
+ * non-serializable properties via ViewChild.
9
+ */
10
+ export declare class AdaptCap implements AfterViewInit, OnChanges, OnDestroy {
11
+ private zone;
12
+ private elRef;
13
+ automationId: string;
14
+ client: AutomationClient;
15
+ workerCount?: number;
16
+ i18n?: CapWidgetI18n;
17
+ darkMode?: boolean;
18
+ solve: EventEmitter<{
19
+ token: string;
20
+ expires: Date;
21
+ }>;
22
+ error: EventEmitter<Error>;
23
+ private initialized;
24
+ constructor(zone: NgZone);
25
+ ngAfterViewInit(): void;
26
+ ngOnChanges(): void;
27
+ ngOnDestroy(): void;
28
+ private sync;
29
+ static ɵfac: i0.ɵɵFactoryDeclaration<AdaptCap, never>;
30
+ static ɵcmp: i0.ɵɵComponentDeclaration<AdaptCap, "mb-adapt-cap", never, { "automationId": { "alias": "automationId"; "required": true; }; "client": { "alias": "client"; "required": true; }; "workerCount": { "alias": "workerCount"; "required": false; }; "i18n": { "alias": "i18n"; "required": false; }; "darkMode": { "alias": "darkMode"; "required": false; }; }, { "solve": "solve"; "error": "error"; }, never, never, true, never>;
31
+ }
@@ -0,0 +1,82 @@
1
+ import { Component, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, Input, Output, ViewChild, } from "@angular/core";
2
+ import { AdaptCapElement } from "@mochabug/adapt-web/cap";
3
+ import * as i0 from "@angular/core";
4
+ // Ensure custom element is registered
5
+ void AdaptCapElement;
6
+ /**
7
+ * Angular component for Cap.js proof-of-work challenges.
8
+ * Renders the `<adapt-cap>` custom element internally and syncs
9
+ * non-serializable properties via ViewChild.
10
+ */
11
+ export class AdaptCap {
12
+ constructor(zone) {
13
+ this.zone = zone;
14
+ this.solve = new EventEmitter();
15
+ this.error = new EventEmitter();
16
+ this.initialized = false;
17
+ }
18
+ ngAfterViewInit() {
19
+ this.sync();
20
+ this.initialized = true;
21
+ }
22
+ ngOnChanges() {
23
+ if (this.initialized) {
24
+ this.sync();
25
+ }
26
+ }
27
+ ngOnDestroy() {
28
+ // Custom element handles cleanup
29
+ }
30
+ sync() {
31
+ const el = this.elRef.nativeElement;
32
+ // Attributes
33
+ el.setAttribute("automation-id", this.automationId);
34
+ if (this.workerCount !== undefined) {
35
+ el.setAttribute("worker-count", String(this.workerCount));
36
+ }
37
+ else {
38
+ el.removeAttribute("worker-count");
39
+ }
40
+ if (this.darkMode) {
41
+ el.setAttribute("dark-mode", "");
42
+ }
43
+ else {
44
+ el.removeAttribute("dark-mode");
45
+ }
46
+ // Non-serializable properties
47
+ el.client = this.client;
48
+ el.i18n = this.i18n;
49
+ // Callbacks — run inside NgZone for change detection
50
+ el.onSolveCallback = (token, expires) => {
51
+ this.zone.run(() => this.solve.emit({ token, expires }));
52
+ };
53
+ el.onErrorCallback = (error) => {
54
+ this.zone.run(() => this.error.emit(error));
55
+ };
56
+ }
57
+ }
58
+ AdaptCap.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AdaptCap, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
59
+ AdaptCap.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.4", type: AdaptCap, isStandalone: true, selector: "mb-adapt-cap", inputs: { automationId: "automationId", client: "client", workerCount: "workerCount", i18n: "i18n", darkMode: "darkMode" }, outputs: { solve: "solve", error: "error" }, viewQueries: [{ propertyName: "elRef", first: true, predicate: ["el"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `<adapt-cap #el></adapt-cap>`, isInline: true, styles: [":host { display: block; }", "adapt-cap { display: block; width: 100%; height: 100%; }"] });
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AdaptCap, decorators: [{
61
+ type: Component,
62
+ args: [{ selector: "mb-adapt-cap", standalone: true, schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<adapt-cap #el></adapt-cap>`, styles: [":host { display: block; }", "adapt-cap { display: block; width: 100%; height: 100%; }"] }]
63
+ }], ctorParameters: () => [{ type: i0.NgZone }], propDecorators: { elRef: [{
64
+ type: ViewChild,
65
+ args: ["el", { static: true }]
66
+ }], automationId: [{
67
+ type: Input,
68
+ args: [{ required: true }]
69
+ }], client: [{
70
+ type: Input,
71
+ args: [{ required: true }]
72
+ }], workerCount: [{
73
+ type: Input
74
+ }], i18n: [{
75
+ type: Input
76
+ }], darkMode: [{
77
+ type: Input
78
+ }], solve: [{
79
+ type: Output
80
+ }], error: [{
81
+ type: Output
82
+ }] } });
@@ -0,0 +1,3 @@
1
+ import "@mochabug/adapt-web/cap";
2
+ export { AdaptCap } from "./adapt-cap.component.js";
3
+ export { Cap as AdaptCapWidget, type AdaptCapWidgetOptions, createChallenge, redeemChallenge, AdaptCapElement, createConnectClient, type ChallengeInfo, type RedeemedChallenge, type CapWidgetI18n, type CapWidgetOptions, } from "@mochabug/adapt-web/cap";
@@ -0,0 +1,4 @@
1
+ // Cap entry — standalone Cap.js widget for Angular
2
+ import "@mochabug/adapt-web/cap";
3
+ export { AdaptCap } from "./adapt-cap.component.js";
4
+ export { Cap as AdaptCapWidget, createChallenge, redeemChallenge, AdaptCapElement, createConnectClient, } from "@mochabug/adapt-web/cap";
@@ -0,0 +1,2 @@
1
+ export * from "@mochabug/adapt-web/core";
2
+ export { AdaptAutomation } from "./adapt-automation.component.js";
@@ -0,0 +1,3 @@
1
+ // Core entry — Adapt component without Cap.js
2
+ export * from "@mochabug/adapt-web/core";
3
+ export { AdaptAutomation } from "./adapt-automation.component.js";
@@ -0,0 +1,2 @@
1
+ export * from "@mochabug/adapt-web";
2
+ export { AdaptAutomation } from "./adapt-automation.component.js";
@@ -0,0 +1,4 @@
1
+ // Re-export everything from web
2
+ export * from "@mochabug/adapt-web";
3
+ // Re-export the Angular component
4
+ export { AdaptAutomation } from "./adapt-automation.component.js";
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@mochabug/adapt-angular",
3
+ "version": "1.0.1-rc.9",
4
+ "description": "Angular component for Adapt automation platform",
5
+ "type": "module",
6
+ "main": "./dist/esm/index.js",
7
+ "types": "./dist/esm/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/esm/index.d.ts",
11
+ "import": "./dist/esm/index.js"
12
+ },
13
+ "./core": {
14
+ "types": "./dist/esm/core.d.ts",
15
+ "import": "./dist/esm/core.js"
16
+ },
17
+ "./cap": {
18
+ "types": "./dist/esm/cap.d.ts",
19
+ "import": "./dist/esm/cap.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "build:esm": "ngc --project tsconfig.esm.json",
27
+ "build": "(cd ../web && npm run build) && rm -rf dist && npm run build:esm",
28
+ "sample": "npm run build && cd sample && npm run dev"
29
+ },
30
+ "keywords": [
31
+ "adapt",
32
+ "automations",
33
+ "mochabug",
34
+ "angular"
35
+ ],
36
+ "author": "mochabug AB",
37
+ "license": "ISC",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "peerDependencies": {
42
+ "@angular/core": "^19.0.0 || ^20.0.0 || ^21.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@angular/core": "^21.2.4",
46
+ "@angular/compiler": "^21.2.4",
47
+ "@angular/compiler-cli": "^21.2.4",
48
+ "rxjs": "^7.8.2",
49
+ "typescript": "~5.9.3",
50
+ "zone.js": "^0.16.1"
51
+ },
52
+ "dependencies": {
53
+ "@mochabug/adapt-web": "*"
54
+ }
55
+ }