@lightworkai.official/debug-capture-angular 0.6.0 → 0.7.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 +1 -1
- package/dist/index.d.ts +19 -8
- package/dist/index.mjs +52 -1515
- package/package.json +2 -2
- package/src/index.ts +56 -22
- package/dist/components/icon.component.d.ts +0 -15
- package/dist/components/icons.d.ts +0 -29
- package/dist/components/image-annotator-dialog.component.d.ts +0 -70
- package/dist/components/my-tickets-panel.component.d.ts +0 -100
- package/dist/components/rich-reply-editor.component.d.ts +0 -89
- package/dist/components/ticket-body.component.d.ts +0 -25
- package/dist/components/ticket-detail.component.d.ts +0 -125
- package/dist/components/ticket-table.component.d.ts +0 -75
- package/src/components/icon.component.ts +0 -24
- package/src/components/icons.ts +0 -64
- package/src/components/image-annotator-dialog.component.ts +0 -159
- package/src/components/my-tickets-panel.component.ts +0 -326
- package/src/components/rich-reply-editor.component.ts +0 -287
- package/src/components/ticket-body.component.ts +0 -53
- package/src/components/ticket-detail.component.ts +0 -441
- package/src/components/ticket-table.component.ts +0 -183
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightworkai.official/debug-capture-angular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Angular bindings for @lightworkai.official/debug-capture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@angular/common": ">=16",
|
|
28
28
|
"@angular/forms": ">=16",
|
|
29
29
|
"@angular/platform-browser": ">=16",
|
|
30
|
-
"@lightworkai.official/debug-capture": "^0.
|
|
30
|
+
"@lightworkai.official/debug-capture": "^0.7.0",
|
|
31
31
|
"@tiptap/core": "^3",
|
|
32
32
|
"@tiptap/starter-kit": "^3",
|
|
33
33
|
"@tiptap/extension-image": "^3",
|
package/src/index.ts
CHANGED
|
@@ -10,21 +10,8 @@
|
|
|
10
10
|
* never sees an unknown tag to complain about. That awkwardness is the
|
|
11
11
|
* wrapper's to absorb, which is most of the reason the wrapper exists.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
Component,
|
|
16
|
-
Input,
|
|
17
|
-
type EnvironmentProviders,
|
|
18
|
-
makeEnvironmentProviders,
|
|
19
|
-
} from "@angular/core";
|
|
20
|
-
import {
|
|
21
|
-
configureDebugCapture,
|
|
22
|
-
installDebugCapture,
|
|
23
|
-
launch,
|
|
24
|
-
setErroredQueriesSource,
|
|
25
|
-
type DebugCaptureConfig,
|
|
26
|
-
type ErroredQuery,
|
|
27
|
-
} from "@lightworkai.official/debug-capture";
|
|
13
|
+
import { APP_INITIALIZER, Component, Input, type EnvironmentProviders, makeEnvironmentProviders, Output, EventEmitter } from "@angular/core";
|
|
14
|
+
import { configureDebugCapture, installDebugCapture, launch, setErroredQueriesSource, type DebugCaptureConfig, type ErroredQuery, openSupportPortal, showToast } from "@lightworkai.official/debug-capture";
|
|
28
15
|
|
|
29
16
|
export interface DebugCaptureProviderOptions extends DebugCaptureConfig {
|
|
30
17
|
/** Failed queries from the app's data layer, if it has one. */
|
|
@@ -98,6 +85,50 @@ export class ReportProblemButtonComponent {
|
|
|
98
85
|
}
|
|
99
86
|
}
|
|
100
87
|
|
|
88
|
+
/**
|
|
89
|
+
* "ดูปัญหาที่แจ้ง" — hands the current user to their tickets on the support host,
|
|
90
|
+
* already signed in.
|
|
91
|
+
*
|
|
92
|
+
* Unlike the reporter button this one awaits, so it CAN be routed through a
|
|
93
|
+
* Promise: the token comes from the host's own server at click time, because it
|
|
94
|
+
* lives about two minutes and is spent on arrival. Hence a pending state, and a
|
|
95
|
+
* failure path — most often "nobody is signed in", which the host answers.
|
|
96
|
+
*/
|
|
97
|
+
@Component({
|
|
98
|
+
selector: "lw-support-portal-button",
|
|
99
|
+
standalone: true,
|
|
100
|
+
template: `
|
|
101
|
+
<button type="button" [attr.aria-label]="label" [attr.title]="label" [disabled]="pending" (click)="open()">
|
|
102
|
+
<ng-content>{{ label }}</ng-content>
|
|
103
|
+
</button>
|
|
104
|
+
`,
|
|
105
|
+
styles: [":host { display: inline-flex; }"],
|
|
106
|
+
})
|
|
107
|
+
export class SupportPortalButtonComponent {
|
|
108
|
+
@Input() label = "ดูปัญหาที่แจ้ง";
|
|
109
|
+
/** Open in a new tab instead of navigating away. */
|
|
110
|
+
@Input() newTab = false;
|
|
111
|
+
@Output() failed = new EventEmitter<unknown>();
|
|
112
|
+
|
|
113
|
+
pending = false;
|
|
114
|
+
|
|
115
|
+
async open(): Promise<void> {
|
|
116
|
+
if (this.pending) return;
|
|
117
|
+
this.pending = true;
|
|
118
|
+
try {
|
|
119
|
+
await openSupportPortal(this.newTab ? { target: "_blank" } : {});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// A host that listens says it its own way; one that does not still gets
|
|
122
|
+
// told, rather than a button that looks broken.
|
|
123
|
+
this.failed.emit(err);
|
|
124
|
+
showToast("เปิดรายการปัญหาไม่สำเร็จ", "error");
|
|
125
|
+
} finally {
|
|
126
|
+
// Only reached when navigation did not happen.
|
|
127
|
+
this.pending = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
101
132
|
/**
|
|
102
133
|
* Real standalone components now, not a web element mounted from a wrapper.
|
|
103
134
|
* The core package holds the logic every framework shares — the ingest client,
|
|
@@ -108,12 +139,15 @@ export class ReportProblemButtonComponent {
|
|
|
108
139
|
*
|
|
109
140
|
* @import "@lightworkai.official/debug-capture/style.css";
|
|
110
141
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
/*
|
|
143
|
+
* The reporter's own tickets are no longer components this package ships.
|
|
144
|
+
*
|
|
145
|
+
* They were: a list, a thread, a reply editor and an annotator dialog, in three
|
|
146
|
+
* frameworks, over a read API. It worked, and it meant every change to the way
|
|
147
|
+
* a ticket looks had to land in four repositories. The support host already
|
|
148
|
+
* renders that screen, so the library hands people to it instead — one button.
|
|
149
|
+
* See SupportPortalButton, and the core's `openSupportPortal`.
|
|
150
|
+
*/
|
|
117
151
|
|
|
118
152
|
export { configureDebugCapture, launch, onCrash } from "@lightworkai.official/debug-capture";
|
|
119
|
-
export type { DebugCaptureConfig, DebugReason, ErroredQuery
|
|
153
|
+
export type { DebugCaptureConfig, DebugReason, ErroredQuery } from "@lightworkai.official/debug-capture";
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,70 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
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 {};
|
|
@@ -1,25 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
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 {};
|
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Component, Input } from "@angular/core";
|
|
2
|
-
import { DomSanitizer, type SafeHtml } from "@angular/platform-browser";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Renders one of our own SVG constants.
|
|
6
|
-
*
|
|
7
|
-
* `bypassSecurityTrustHtml` is safe HERE and only here: the markup comes from
|
|
8
|
-
* `ICONS`, a frozen table in this package's own source. Nothing a user or a
|
|
9
|
-
* server ever wrote reaches it — the thread's HTML goes through the core's
|
|
10
|
-
* allowlist walker instead.
|
|
11
|
-
*/
|
|
12
|
-
@Component({
|
|
13
|
-
selector: "lw-icon",
|
|
14
|
-
standalone: true,
|
|
15
|
-
template: `<span [innerHTML]="safe"></span>`,
|
|
16
|
-
styles: [":host { display: inline-flex; align-items: center; }"],
|
|
17
|
-
})
|
|
18
|
-
export class IconComponent {
|
|
19
|
-
@Input({ required: true }) set svg(value: string) {
|
|
20
|
-
this.safe = this.sanitizer.bypassSecurityTrustHtml(value);
|
|
21
|
-
}
|
|
22
|
-
protected safe: SafeHtml = "";
|
|
23
|
-
constructor(private readonly sanitizer: DomSanitizer) {}
|
|
24
|
-
}
|