@lightworkai.official/debug-capture-angular 0.6.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 ADDED
@@ -0,0 +1,67 @@
1
+ # @lightworkai.official/debug-capture-angular
2
+
3
+ Angular bindings for [`@lightworkai.official/debug-capture`](../debug-capture/README.md).
4
+
5
+ ```ts
6
+ // app.config.ts
7
+ import { provideDebugCapture } from "@lightworkai.official/debug-capture-angular";
8
+
9
+ export const appConfig: ApplicationConfig = {
10
+ providers: [
11
+ provideDebugCapture({
12
+ host: "https://support.example.com",
13
+ realmKey: "pk_live_…",
14
+ app: { version: "1.4.2", environment: "production" },
15
+ user: () => ({ id: me.id, email: me.email, fullName: me.name }),
16
+ }),
17
+ ],
18
+ };
19
+ ```
20
+
21
+ ```html
22
+ <!-- wherever the button belongs -->
23
+ <lw-report-problem-button />
24
+ ```
25
+
26
+ ```html
27
+ <!-- a route of your own: what this person has reported, and where it stands -->
28
+ <lw-my-tickets-panel />
29
+ ```
30
+
31
+ ## Notes
32
+
33
+ **`<lw-my-tickets-panel>` needs `identity` in the config** — a token your
34
+ *server* signs. `realmKey` is public, so it can authorise filing a report but
35
+ never reading one back; see
36
+ [the core README](../debug-capture/README.md#it-needs-to-know-who-is-asking).
37
+ Set `[refreshKey]` to make it re-read the list. Import
38
+ `MyTicketsPanelComponent`; it is standalone.
39
+
40
+ **The panel is a real standalone component**, `<lw-my-tickets-panel>`, with
41
+ `@Input()`s and `@Output()`s. It was a web element mounted in `ngAfterViewInit`,
42
+ which meant Angular could see none of it. The logic it arranges lives in the core
43
+ and is shared with the Vue and React packages.
44
+
45
+ **No `CUSTOM_ELEMENTS_SCHEMA` needed** — nothing here is a custom element any
46
+ more. (The reporter dialog still is, but `installDebugCapture` creates it
47
+ imperatively and it never appears in a template, so the compiler has no unknown
48
+ tag to reject.)
49
+
50
+ **TipTap is a peer dependency.** Angular has no TipTap binding, so
51
+ `RichReplyEditorComponent` drives `@tiptap/core` directly: create the editor onto
52
+ an element ref, destroy it on teardown, mark for check when the selection moves.
53
+
54
+ Import the stylesheet once, in `angular.json` or a global stylesheet:
55
+
56
+ ```css
57
+ @import "@lightworkai.official/debug-capture/style.css";
58
+ ```
59
+
60
+ **`provideDebugCapture` configures and arms in one call**, as an
61
+ `APP_INITIALIZER`, so the buffers are recording before the first route renders.
62
+ Capture that starts late misses the navigation the user is about to complain
63
+ about.
64
+
65
+ **The button calls `launch` synchronously.** Screen capture is gated on a user
66
+ gesture, which rules out routing this through a service that returns a Promise —
67
+ anything awaited between the click and `getDisplayMedia` loses the gesture.
@@ -0,0 +1,15 @@
1
+ import { DomSanitizer, type SafeHtml } from "@angular/platform-browser";
2
+ /**
3
+ * Renders one of our own SVG constants.
4
+ *
5
+ * `bypassSecurityTrustHtml` is safe HERE and only here: the markup comes from
6
+ * `ICONS`, a frozen table in this package's own source. Nothing a user or a
7
+ * server ever wrote reaches it — the thread's HTML goes through the core's
8
+ * allowlist walker instead.
9
+ */
10
+ export declare class IconComponent {
11
+ private readonly sanitizer;
12
+ set svg(value: string);
13
+ protected safe: SafeHtml;
14
+ constructor(sanitizer: DomSanitizer);
15
+ }
@@ -0,0 +1,29 @@
1
+ export declare const ICONS: {
2
+ readonly bold: string;
3
+ readonly italic: string;
4
+ readonly underline: string;
5
+ readonly bulletList: string;
6
+ readonly orderedList: string;
7
+ readonly link: string;
8
+ readonly image: string;
9
+ readonly send: string;
10
+ readonly search: string;
11
+ readonly arrowLeft: string;
12
+ readonly check: string;
13
+ readonly message: string;
14
+ readonly rotate: string;
15
+ readonly alert: string;
16
+ readonly inbox: string;
17
+ readonly clock: string;
18
+ readonly checkCircle: string;
19
+ readonly ban: string;
20
+ readonly wrench: string;
21
+ readonly square: string;
22
+ readonly arrowUpRight: string;
23
+ readonly highlighter: string;
24
+ readonly type: string;
25
+ readonly trash: string;
26
+ readonly pencil: string;
27
+ readonly checkSmall: string;
28
+ readonly xSmall: string;
29
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Draw on an image before it is sent.
3
+ *
4
+ * The original opens this the moment you pick a picture, and the reason is in
5
+ * its own comment: annotating BEFORE upload keeps the canvas same-origin, so
6
+ * `toDataURL` is not tainted by a cross-origin presigned URL. Pointing at the
7
+ * problem is also most of what a screenshot is for.
8
+ *
9
+ * The drawing itself is `createAnnotator` from the core — one canvas engine,
10
+ * shared by all three framework packages, because there is no declarative way
11
+ * to draw on a canvas. What is here is only the chrome.
12
+ */
13
+ import { EventEmitter, type AfterViewInit, type OnDestroy } from "@angular/core";
14
+ import { type Annotator, type AnnotatorTool, type ReporterStrings } from "@lightworkai.official/debug-capture";
15
+ export declare class ImageAnnotatorDialogComponent implements AfterViewInit, OnDestroy {
16
+ /** The picked image, as a same-origin data URI. */
17
+ src: string;
18
+ /** Shown in the description, as the app's dialog does. */
19
+ filename?: string;
20
+ locale?: "th" | "en";
21
+ /** The annotated image, at full resolution. */
22
+ confirmed: EventEmitter<string>;
23
+ cancelled: EventEmitter<void>;
24
+ private canvas;
25
+ protected annotator: Annotator | null;
26
+ protected active: AnnotatorTool | null;
27
+ protected loading: boolean;
28
+ protected readonly icons: {
29
+ readonly bold: string;
30
+ readonly italic: string;
31
+ readonly underline: string;
32
+ readonly bulletList: string;
33
+ readonly orderedList: string;
34
+ readonly link: string;
35
+ readonly image: string;
36
+ readonly send: string;
37
+ readonly search: string;
38
+ readonly arrowLeft: string;
39
+ readonly check: string;
40
+ readonly message: string;
41
+ readonly rotate: string;
42
+ readonly alert: string;
43
+ readonly inbox: string;
44
+ readonly clock: string;
45
+ readonly checkCircle: string;
46
+ readonly ban: string;
47
+ readonly wrench: string;
48
+ readonly square: string;
49
+ readonly arrowUpRight: string;
50
+ readonly highlighter: string;
51
+ readonly type: string;
52
+ readonly trash: string;
53
+ readonly pencil: string;
54
+ readonly checkSmall: string;
55
+ readonly xSmall: string;
56
+ };
57
+ protected readonly tools: {
58
+ tool: AnnotatorTool;
59
+ key: keyof ReporterStrings;
60
+ icon: string;
61
+ }[];
62
+ protected get t(): ReporterStrings;
63
+ protected label(key: keyof ReporterStrings): string;
64
+ ngAfterViewInit(): void;
65
+ ngOnDestroy(): void;
66
+ protected pick(tool: AnnotatorTool): void;
67
+ protected clear(): void;
68
+ protected backdrop(event: MouseEvent): void;
69
+ protected confirm(): void;
70
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * "ปัญหาที่แจ้ง" — the reports this person filed, and what became of them.
3
+ *
4
+ * Put it on a route of your own — the reporter-facing counterpart to the
5
+ * agent's ticket list. It needs `identity` in the config, because the widget's
6
+ * realm key is public and can authorise filing a report but never reading one
7
+ * back — see the core README.
8
+ *
9
+ * This component owns fetching and view state and nothing else: the columns,
10
+ * the sorting, the sanitising and the dates all live in the core, shared with
11
+ * the Vue and React packages.
12
+ */
13
+ import { EventEmitter, type OnChanges, type OnInit } from "@angular/core";
14
+ import { type MyTicketDetail, type MyTicketListItem, type RealmConfig, type Sort, type SortKey, type StatusEvent, type StatusMaps, type TicketStrings } from "@lightworkai.official/debug-capture";
15
+ export declare class MyTicketsPanelComponent implements OnInit, OnChanges {
16
+ /** Change it to re-read the list — after filing a report, say. */
17
+ refreshKey?: string | number;
18
+ /** Hide the panel's own heading when the host page already has one. */
19
+ hideTitle: boolean;
20
+ replied: EventEmitter<MyTicketDetail>;
21
+ reopenedTicket: EventEmitter<MyTicketDetail>;
22
+ errored: EventEmitter<string>;
23
+ protected readonly icons: {
24
+ readonly bold: string;
25
+ readonly italic: string;
26
+ readonly underline: string;
27
+ readonly bulletList: string;
28
+ readonly orderedList: string;
29
+ readonly link: string;
30
+ readonly image: string;
31
+ readonly send: string;
32
+ readonly search: string;
33
+ readonly arrowLeft: string;
34
+ readonly check: string;
35
+ readonly message: string;
36
+ readonly rotate: string;
37
+ readonly alert: string;
38
+ readonly inbox: string;
39
+ readonly clock: string;
40
+ readonly checkCircle: string;
41
+ readonly ban: string;
42
+ readonly wrench: string;
43
+ readonly square: string;
44
+ readonly arrowUpRight: string;
45
+ readonly highlighter: string;
46
+ readonly type: string;
47
+ readonly trash: string;
48
+ readonly pencil: string;
49
+ readonly checkSmall: string;
50
+ readonly xSmall: string;
51
+ };
52
+ protected items: MyTicketListItem[];
53
+ protected config: RealmConfig | null;
54
+ protected loading: boolean;
55
+ protected error: string | null;
56
+ protected noIdentity: boolean;
57
+ protected keyword: string;
58
+ protected applied: string;
59
+ protected statusFilter: string;
60
+ protected sort: Sort;
61
+ protected page: number;
62
+ protected shown: number;
63
+ protected detail: MyTicketDetail | null;
64
+ protected history: StatusEvent[];
65
+ protected detailError: string | null;
66
+ protected detailLoading: boolean;
67
+ protected sending: boolean;
68
+ protected reopening: boolean;
69
+ private debounce;
70
+ private first;
71
+ protected get locale(): "th" | "en" | undefined;
72
+ protected get host(): string;
73
+ protected get t(): TicketStrings;
74
+ protected get maps(): StatusMaps;
75
+ ngOnInit(): void;
76
+ ngOnChanges(): void;
77
+ protected load(): Promise<void>;
78
+ /** Debounced: re-filtering per keystroke resets the page under the reader's hands. */
79
+ protected onKeyword(next: string): void;
80
+ protected onStatus(next: string): void;
81
+ /** Open a DIFFERENT ticket: tear the view down, because nothing on screen is its. */
82
+ protected open(id: string): Promise<MyTicketDetail | null>;
83
+ /**
84
+ * Re-read the ticket we are already looking at, in place.
85
+ *
86
+ * Deliberately separate from `open()`. That one clears the detail and raises
87
+ * the loading flag, which destroys the whole view and rebuilds it — so
88
+ * sending a reply threw the reader back to the top of the page, away from the
89
+ * conversation they were in the middle of. It reads as the page reloading,
90
+ * because visually that is what happens.
91
+ */
92
+ protected reload(id: string): Promise<MyTicketDetail | null>;
93
+ protected back(): void;
94
+ protected toggleSort(key: SortKey): void;
95
+ protected send(body: string): Promise<void>;
96
+ protected reopen(note: string): Promise<void>;
97
+ protected readonly uploadImage: (file: File) => Promise<string>;
98
+ protected readonly attachmentUrl: (id: string) => Promise<string>;
99
+ private safe;
100
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The reporter's reply box — TipTap, the same editor the original gives them.
3
+ *
4
+ * TipTap ships no Angular binding, so this drives `@tiptap/core` directly:
5
+ * create the editor onto an element ref, destroy it on teardown, and ask it for
6
+ * state when Angular renders. That is a dozen lines, and it is still far less
7
+ * than re-implementing selection handling, list nesting and paste sanitising by
8
+ * hand — which is what this was before, and what it should never have been.
9
+ */
10
+ import { ChangeDetectorRef, EventEmitter, type AfterViewInit, type OnDestroy } from "@angular/core";
11
+ /**
12
+ * The toolbar's words.
13
+ *
14
+ * An open map because the toolbar is configurable — a host asking for `strike`
15
+ * needs a label for it, and enumerating every control here would mean editing
16
+ * this type every time one is added.
17
+ */
18
+ export type EditorLabels = Record<string, string>;
19
+ interface Tool {
20
+ key: keyof EditorLabels;
21
+ icon: string;
22
+ mark: string;
23
+ run: () => void;
24
+ separatorBefore?: boolean;
25
+ }
26
+ export declare class RichReplyEditorComponent implements AfterViewInit, OnDestroy {
27
+ private readonly cdr;
28
+ placeholder: string;
29
+ /** Support host origin — the sanitiser resolves inline image URLs against it. */
30
+ host: string;
31
+ labels: EditorLabels;
32
+ uploadImage: (file: File) => Promise<string>;
33
+ set disabledState(value: boolean);
34
+ /** UI language, for the annotator's own labels. */
35
+ locale?: "th" | "en";
36
+ /** Content to start from — editing an existing message rather than writing one. */
37
+ initial?: string;
38
+ errored: EventEmitter<string>;
39
+ /**
40
+ * The image waiting to be drawn on, as a same-origin data URI. Non-null means
41
+ * the annotator is open.
42
+ *
43
+ * Every route in — the picker, a paste, a drop — lands here first. The
44
+ * original does the same: pointing at the problem is most of what a
45
+ * screenshot is for, and annotating BEFORE upload keeps the canvas
46
+ * same-origin, so `toDataURL` is not tainted by a presigned URL.
47
+ */
48
+ protected pending: string | null;
49
+ protected pendingName: string;
50
+ protected disabled: boolean;
51
+ private editor;
52
+ private mount;
53
+ private file;
54
+ protected readonly tools: Tool[];
55
+ constructor(cdr: ChangeDetectorRef);
56
+ ngAfterViewInit(): void;
57
+ ngOnDestroy(): void;
58
+ /**
59
+ * Fired on `mousedown` with the default prevented, not on `click`: a toolbar
60
+ * button taking focus collapses the editor's selection, so by the time
61
+ * `click` fires the command has nothing to apply to.
62
+ */
63
+ protected press(event: MouseEvent, tool: Tool): void;
64
+ protected isActive(mark: string): boolean;
65
+ protected onFile(event: Event): void;
66
+ /**
67
+ * Open the annotator on a picked image rather than inserting it straight away.
68
+ *
69
+ * One dialog, everywhere. There was briefly a config hook letting a host swap
70
+ * in its own — which solved the symptom (two dialogs that looked different)
71
+ * by blessing the cause (two dialogs).
72
+ */
73
+ private addImage;
74
+ /** The annotated image comes back as a data URI; upload THAT and insert it. */
75
+ protected commitImage(dataUri: string): Promise<void>;
76
+ private toggleLink;
77
+ /**
78
+ * Sanitised HTML, or "" when there is nothing worth sending.
79
+ *
80
+ * `cleanHtml` is not belt-and-braces over TipTap's schema. The schema drops
81
+ * an `onclick`, but the Image extension takes any `src` it is given — so a
82
+ * screenshot pasted from a web page arrives with that page's remote image
83
+ * intact, and posting it would store a tracking pixel in the thread aimed at
84
+ * every future reader.
85
+ */
86
+ value(): string;
87
+ clear(): void;
88
+ }
89
+ export {};
@@ -0,0 +1,25 @@
1
+ import { type OnChanges } from "@angular/core";
2
+ /**
3
+ * A message or description body, rendered safely.
4
+ *
5
+ * Deliberately NOT `[innerHTML]`, even though Angular sanitises that: bodies
6
+ * are HTML written by other people, and the core's walker is the one place all
7
+ * three framework packages agree on what an inline image may point at. Angular
8
+ * would keep a remote `<img>`, which is how a ticket becomes a tracking pixel
9
+ * aimed at whoever opens it.
10
+ */
11
+ export declare class TicketBodyComponent implements OnChanges {
12
+ html: string;
13
+ host: string;
14
+ private target;
15
+ /**
16
+ * The image being viewed full size, if any.
17
+ *
18
+ * Bodies are capped to a thumbnail so one screenshot cannot push a whole
19
+ * conversation off the screen — which is only reasonable if the full size is
20
+ * a click away.
21
+ */
22
+ protected zoomed: string | null;
23
+ protected onClick(event: MouseEvent): void;
24
+ ngOnChanges(): void;
25
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * One report: what was filed, where it stands, what happened, and the thread.
3
+ * The order is the original's.
4
+ */
5
+ import { EventEmitter, type OnChanges } from "@angular/core";
6
+ import { type MyTicketDetail, type RealmConfig, type StatusEvent, type StatusMaps, type TicketStrings } from "@lightworkai.official/debug-capture";
7
+ interface TimelineNode {
8
+ label: string;
9
+ time: string;
10
+ state: "done" | "current" | "pending";
11
+ reopened: boolean;
12
+ }
13
+ export declare class TicketDetailComponent implements OnChanges {
14
+ ticket: MyTicketDetail;
15
+ history: StatusEvent[];
16
+ config: RealmConfig;
17
+ maps: StatusMaps;
18
+ t: TicketStrings;
19
+ host: string;
20
+ locale?: "th" | "en";
21
+ sending: boolean;
22
+ reopening: boolean;
23
+ uploadImage: (file: File) => Promise<string>;
24
+ set attachmentUrl(read: (id: string) => Promise<string>);
25
+ back: EventEmitter<void>;
26
+ sent: EventEmitter<string>;
27
+ reopened: EventEmitter<string>;
28
+ edited: EventEmitter<void>;
29
+ errored: EventEmitter<string>;
30
+ /**
31
+ * The message being rewritten, if any.
32
+ *
33
+ * Only ONE at a time: an open editor per bubble would let someone start three
34
+ * rewrites and lose track of which are unsaved.
35
+ */
36
+ protected editingId: string | null;
37
+ protected savingEdit: boolean;
38
+ private draft?;
39
+ protected saveEdit(messageId: string): Promise<void>;
40
+ private editor?;
41
+ private thread?;
42
+ private seenMessages;
43
+ /**
44
+ * Open at the newest message, and stay there when one arrives.
45
+ *
46
+ * The thread is a fixed-height window (~320px), so without the first part it
47
+ * opens showing the OLDEST messages and the reader has to scroll to find out
48
+ * what has happened since. Every chat opens at the bottom.
49
+ *
50
+ * The second part matters because the thread sits above the composer: a reply
51
+ * pushes the composer down and out of sight, leaving the reader in the middle
52
+ * of the conversation with no sign their message landed.
53
+ */
54
+ ngOnChanges(): void;
55
+ protected readonly icons: {
56
+ readonly bold: string;
57
+ readonly italic: string;
58
+ readonly underline: string;
59
+ readonly bulletList: string;
60
+ readonly orderedList: string;
61
+ readonly link: string;
62
+ readonly image: string;
63
+ readonly send: string;
64
+ readonly search: string;
65
+ readonly arrowLeft: string;
66
+ readonly check: string;
67
+ readonly message: string;
68
+ readonly rotate: string;
69
+ readonly alert: string;
70
+ readonly inbox: string;
71
+ readonly clock: string;
72
+ readonly checkCircle: string;
73
+ readonly ban: string;
74
+ readonly wrench: string;
75
+ readonly square: string;
76
+ readonly arrowUpRight: string;
77
+ readonly highlighter: string;
78
+ readonly type: string;
79
+ readonly trash: string;
80
+ readonly pencil: string;
81
+ readonly checkSmall: string;
82
+ readonly xSmall: string;
83
+ };
84
+ protected reopenOpen: boolean;
85
+ protected shotUrls: Record<string, string>;
86
+ private readUrl?;
87
+ protected get lang(): "th" | "en";
88
+ protected get key(): string;
89
+ protected get updated(): boolean;
90
+ protected get awaiting(): boolean;
91
+ protected get tone(): {
92
+ icon: string;
93
+ border: string;
94
+ bg: string;
95
+ fg: string;
96
+ accent: string;
97
+ };
98
+ protected get kicker(): string;
99
+ /**
100
+ * Config-first: an awaiting lane's "please reply" wins, then the lane's own
101
+ * description (so a realm's custom lane carries its own words), then our
102
+ * default copy, then the bare label.
103
+ */
104
+ protected get headline(): string;
105
+ protected get showNote(): boolean;
106
+ protected get canReopen(): boolean;
107
+ protected get reopenPlaceholder(): string;
108
+ protected get editorLabels(): {
109
+ bold: string;
110
+ italic: string;
111
+ underline: string;
112
+ bulletList: string;
113
+ orderedList: string;
114
+ link: string;
115
+ linkPrompt: string;
116
+ attachImage: string;
117
+ };
118
+ protected get screenshots(): import("@lightworkai.official/debug-capture").TicketAttachment[];
119
+ /** The lifecycle, chronologically — reopens included. */
120
+ protected get timeline(): TimelineNode[];
121
+ protected when(iso: string | null): string;
122
+ protected send(): void;
123
+ private loadShots;
124
+ }
125
+ export {};
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The reporter's list — the original's seven columns.
3
+ *
4
+ * Every decision here is imported: which rows survive the filter, how a column
5
+ * sorts, what "the team replied" means, how a date reads. That is the point of
6
+ * the split. This file arranges them; it decides nothing.
7
+ */
8
+ import { EventEmitter } from "@angular/core";
9
+ import { type MyTicketListItem, type Sort, type SortKey, type StatusMaps, type TicketStrings } from "@lightworkai.official/debug-capture";
10
+ export declare class TicketTableComponent {
11
+ items: MyTicketListItem[];
12
+ maps: StatusMaps;
13
+ t: TicketStrings;
14
+ slug?: string;
15
+ timezone: string;
16
+ locale?: "th" | "en";
17
+ keyword: string;
18
+ statusFilter: string;
19
+ sort: Sort;
20
+ page: number;
21
+ opened: EventEmitter<string>;
22
+ sorted: EventEmitter<SortKey>;
23
+ paged: EventEmitter<number>;
24
+ counted: EventEmitter<number>;
25
+ protected readonly icons: {
26
+ readonly bold: string;
27
+ readonly italic: string;
28
+ readonly underline: string;
29
+ readonly bulletList: string;
30
+ readonly orderedList: string;
31
+ readonly link: string;
32
+ readonly image: string;
33
+ readonly send: string;
34
+ readonly search: string;
35
+ readonly arrowLeft: string;
36
+ readonly check: string;
37
+ readonly message: string;
38
+ readonly rotate: string;
39
+ readonly alert: string;
40
+ readonly inbox: string;
41
+ readonly clock: string;
42
+ readonly checkCircle: string;
43
+ readonly ban: string;
44
+ readonly wrench: string;
45
+ readonly square: string;
46
+ readonly arrowUpRight: string;
47
+ readonly highlighter: string;
48
+ readonly type: string;
49
+ readonly trash: string;
50
+ readonly pencil: string;
51
+ readonly checkSmall: string;
52
+ readonly xSmall: string;
53
+ };
54
+ protected readonly columns: {
55
+ key: SortKey;
56
+ label: keyof TicketStrings;
57
+ width?: string;
58
+ }[];
59
+ protected get visible(): MyTicketListItem[];
60
+ protected get pages(): number;
61
+ /** A filter can strand the reader past the end of the shorter list. */
62
+ protected get current(): number;
63
+ protected get rows(): MyTicketListItem[];
64
+ protected label(key: SortKey): string;
65
+ protected arrow(key: SortKey): string;
66
+ protected ariaSort(key: SortKey): string | null;
67
+ protected key(row: MyTicketListItem): string;
68
+ protected tone(status: string): {
69
+ fg: string;
70
+ bg: string;
71
+ };
72
+ protected solved(row: MyTicketListItem): boolean;
73
+ protected replied(row: MyTicketListItem): boolean;
74
+ protected when(iso: string | null): string;
75
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Angular bindings for @lightworkai.official/debug-capture.
3
+ *
4
+ * Standalone providers + one standalone component, so this works in an
5
+ * `app.config.ts` and in an NgModule without shipping two of everything.
6
+ *
7
+ * Note what a consumer does NOT have to do: no `CUSTOM_ELEMENTS_SCHEMA`. The
8
+ * reporter is a custom element, but it is created imperatively by
9
+ * `installDebugCapture` and never appears in a template, so Angular's compiler
10
+ * never sees an unknown tag to complain about. That awkwardness is the
11
+ * wrapper's to absorb, which is most of the reason the wrapper exists.
12
+ */
13
+ import { type EnvironmentProviders } from "@angular/core";
14
+ import { type DebugCaptureConfig, type ErroredQuery } from "@lightworkai.official/debug-capture";
15
+ export interface DebugCaptureProviderOptions extends DebugCaptureConfig {
16
+ /** Failed queries from the app's data layer, if it has one. */
17
+ errorQueries?: () => ErroredQuery[];
18
+ /** Mount the built-in reporter UI. Off if the host renders its own. */
19
+ ui?: boolean;
20
+ }
21
+ /**
22
+ * Configure and arm in one call, from `app.config.ts`:
23
+ *
24
+ * providers: [provideDebugCapture({ host, realmKey, app: {...} })]
25
+ *
26
+ * Runs as an APP_INITIALIZER so the buffers are recording before the first
27
+ * route renders. Capture that starts late is capture that misses the navigation
28
+ * the user is about to complain about.
29
+ */
30
+ export declare function provideDebugCapture(options: DebugCaptureProviderOptions): EnvironmentProviders;
31
+ /**
32
+ * The button that opens the reporter.
33
+ *
34
+ * `launch` runs straight out of the click: screen capture is gated on a user
35
+ * gesture, and anything awaited in front of it loses the gesture. That rules
36
+ * out routing this through a service that returns a Promise.
37
+ */
38
+ export declare class ReportProblemButtonComponent {
39
+ label: string;
40
+ report(): void;
41
+ }
42
+ /**
43
+ * Real standalone components now, not a web element mounted from a wrapper.
44
+ * The core package holds the logic every framework shares — the ingest client,
45
+ * the sanitiser, the status maps, the comparators, the capture buffers — and
46
+ * each framework package renders it the way that framework renders things.
47
+ *
48
+ * Import the stylesheet once, in `angular.json` or a global stylesheet:
49
+ *
50
+ * @import "@lightworkai.official/debug-capture/style.css";
51
+ */
52
+ export { MyTicketsPanelComponent } from "./components/my-tickets-panel.component";
53
+ export { TicketTableComponent } from "./components/ticket-table.component";
54
+ export { TicketDetailComponent } from "./components/ticket-detail.component";
55
+ export { RichReplyEditorComponent } from "./components/rich-reply-editor.component";
56
+ export { TicketBodyComponent } from "./components/ticket-body.component";
57
+ export { ImageAnnotatorDialogComponent } from "./components/image-annotator-dialog.component";
58
+ export { configureDebugCapture, launch, onCrash } from "@lightworkai.official/debug-capture";
59
+ export type { DebugCaptureConfig, DebugReason, ErroredQuery, MyTicketListItem, MyTicketDetail } from "@lightworkai.official/debug-capture";