@lightworkai.official/debug-capture-vue 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 +12 -8
- package/dist/components/{ToolbarButton.vue.d.ts → SupportPortalButton.vue.d.ts} +12 -7
- package/dist/index.d.ts +2 -8
- package/dist/index.mjs +762 -2319
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/SupportPortalButton.vue +50 -0
- package/src/index.ts +11 -16
- package/dist/components/ImageAnnotatorDialog.vue.d.ts +0 -19
- package/dist/components/MyTicketsPanel.vue.d.ts +0 -17
- package/dist/components/ReopenPanel.vue.d.ts +0 -12
- package/dist/components/RichReplyEditor.vue.d.ts +0 -59
- package/dist/components/StatusHero.vue.d.ts +0 -10
- package/dist/components/StatusPill.vue.d.ts +0 -16
- package/dist/components/TicketBody.vue.d.ts +0 -6
- package/dist/components/TicketConversation.vue.d.ts +0 -22
- package/dist/components/TicketDetail.vue.d.ts +0 -28
- package/dist/components/TicketScreenshots.vue.d.ts +0 -8
- package/dist/components/TicketTable.vue.d.ts +0 -25
- package/dist/components/TicketTimeline.vue.d.ts +0 -11
- package/dist/components/icons.d.ts +0 -38
- package/src/components/ImageAnnotatorDialog.vue +0 -160
- package/src/components/MyTicketsPanel.vue +0 -312
- package/src/components/ReopenPanel.vue +0 -47
- package/src/components/RichReplyEditor.vue +0 -321
- package/src/components/StatusHero.vue +0 -85
- package/src/components/StatusPill.vue +0 -14
- package/src/components/TicketBody.vue +0 -50
- package/src/components/TicketConversation.vue +0 -227
- package/src/components/TicketDetail.vue +0 -130
- package/src/components/TicketScreenshots.vue +0 -47
- package/src/components/TicketTable.vue +0 -142
- package/src/components/TicketTimeline.vue +0 -86
- package/src/components/ToolbarButton.vue +0 -33
- package/src/components/icons.ts +0 -90
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightworkai.official/debug-capture-vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Vue 3 bindings for @lightworkai.official/debug-capture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"vue": ">=3.3",
|
|
28
|
-
"@lightworkai.official/debug-capture": "^0.
|
|
28
|
+
"@lightworkai.official/debug-capture": "^0.7.0",
|
|
29
29
|
"@tiptap/vue-3": "^3",
|
|
30
30
|
"@tiptap/starter-kit": "^3",
|
|
31
31
|
"@tiptap/extension-image": "^3",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* "ดูปัญหาที่แจ้ง" — hands the current user to their tickets on the support host,
|
|
4
|
+
* already signed in.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the reporter button, this one awaits: the token is fetched from the
|
|
7
|
+
* host's own server at click time, because it lives about two minutes and is
|
|
8
|
+
* spent on arrival. So it can be pending, and it can fail — most often because
|
|
9
|
+
* nobody is signed in, which is the host's question to answer, not ours.
|
|
10
|
+
*
|
|
11
|
+
* Unstyled by default, like its sibling: it lands in the host's own header.
|
|
12
|
+
*/
|
|
13
|
+
import { ref } from "vue";
|
|
14
|
+
import { openSupportPortal, showToast } from "@lightworkai.official/debug-capture";
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(
|
|
17
|
+
defineProps<{
|
|
18
|
+
label?: string;
|
|
19
|
+
/** Open in a new tab instead of navigating away. */
|
|
20
|
+
newTab?: boolean;
|
|
21
|
+
}>(),
|
|
22
|
+
{ label: "ดูปัญหาที่แจ้ง", newTab: false },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const emit = defineEmits<{ (e: "error", err: unknown): void }>();
|
|
26
|
+
const pending = ref(false);
|
|
27
|
+
|
|
28
|
+
async function open(event: MouseEvent): Promise<void> {
|
|
29
|
+
if (event.defaultPrevented || pending.value) return;
|
|
30
|
+
pending.value = true;
|
|
31
|
+
try {
|
|
32
|
+
await openSupportPortal(props.newTab ? { target: "_blank" } : {});
|
|
33
|
+
} catch (err) {
|
|
34
|
+
// A host that listens gets to say it its own way; one that does not still
|
|
35
|
+
// gets told, rather than a button that looks broken.
|
|
36
|
+
if (emit) emit("error", err);
|
|
37
|
+
showToast("เปิดรายการปัญหาไม่สำเร็จ", "error");
|
|
38
|
+
} finally {
|
|
39
|
+
// Only reached when navigation did not happen — on success the page is
|
|
40
|
+
// already leaving.
|
|
41
|
+
pending.value = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<button type="button" :aria-label="label" :title="label" :disabled="pending" @click="open">
|
|
48
|
+
<slot :pending="pending">{{ label }}</slot>
|
|
49
|
+
</button>
|
|
50
|
+
</template>
|
package/src/index.ts
CHANGED
|
@@ -13,22 +13,17 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export { default as DebugCaptureInit } from "./components/DebugCaptureInit.vue";
|
|
15
15
|
export { default as ReportProblemButton } from "./components/ReportProblemButton.vue";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
/*
|
|
17
|
+
* The reporter's own tickets are no longer components this package ships.
|
|
18
|
+
*
|
|
19
|
+
* They were: a list, a thread, a reply editor and an annotator dialog, in three
|
|
20
|
+
* frameworks, over a read API. It worked, and it meant every change to the way
|
|
21
|
+
* a ticket looks had to land in four repositories. The support host already
|
|
22
|
+
* renders that screen, so the library hands people to it instead — one button.
|
|
23
|
+
* See SupportPortalButton, and the core's `openSupportPortal`.
|
|
24
|
+
*/
|
|
25
|
+
export { default as SupportPortalButton } from "./components/SupportPortalButton.vue";
|
|
25
26
|
|
|
26
27
|
// One import for a host, not two.
|
|
27
28
|
export { configureDebugCapture, launch, onCrash } from "@lightworkai.official/debug-capture";
|
|
28
|
-
export type {
|
|
29
|
-
DebugCaptureConfig,
|
|
30
|
-
DebugReason,
|
|
31
|
-
ErroredQuery,
|
|
32
|
-
MyTicketListItem,
|
|
33
|
-
MyTicketDetail,
|
|
34
|
-
} from "@lightworkai.official/debug-capture";
|
|
29
|
+
export type { DebugCaptureConfig, DebugReason, ErroredQuery } from "@lightworkai.official/debug-capture";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
type __VLS_Props = {
|
|
2
|
-
/**
|
|
3
|
-
* The picked image, as a same-origin data URI. Null closes the dialog, so a
|
|
4
|
-
* caller can keep it mounted and drive it from one nullable ref.
|
|
5
|
-
*/
|
|
6
|
-
src: string | null;
|
|
7
|
-
/** Shown in the description, as the app's dialog does. */
|
|
8
|
-
filename?: string;
|
|
9
|
-
/** Falls back to the configured locale — a caller inside the app need not pass it. */
|
|
10
|
-
locale?: "th" | "en";
|
|
11
|
-
};
|
|
12
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
13
|
-
cancel: () => any;
|
|
14
|
-
confirm: (dataUri: string) => any;
|
|
15
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
-
onCancel?: (() => any) | undefined;
|
|
17
|
-
onConfirm?: ((dataUri: string) => any) | undefined;
|
|
18
|
-
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
-
export default _default;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type MyTicketDetail } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
/** Change it to re-read the list — after filing a report, say. */
|
|
4
|
-
refreshKey?: string | number;
|
|
5
|
-
/** Hide the panel's own heading when the host page already has one. */
|
|
6
|
-
hideTitle?: boolean;
|
|
7
|
-
};
|
|
8
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
9
|
-
error: (message: string) => any;
|
|
10
|
-
reopened: (ticket: MyTicketDetail) => any;
|
|
11
|
-
replied: (ticket: MyTicketDetail) => any;
|
|
12
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
-
onError?: ((message: string) => any) | undefined;
|
|
14
|
-
onReopened?: ((ticket: MyTicketDetail) => any) | undefined;
|
|
15
|
-
onReplied?: ((ticket: MyTicketDetail) => any) | undefined;
|
|
16
|
-
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
|
-
export default _default;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { TicketStrings } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
t: TicketStrings;
|
|
4
|
-
locale: "th" | "en";
|
|
5
|
-
busy?: boolean;
|
|
6
|
-
};
|
|
7
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
8
|
-
reopen: (note: string) => any;
|
|
9
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
10
|
-
onReopen?: ((note: string) => any) | undefined;
|
|
11
|
-
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
-
export default _default;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { type EditorTool } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
placeholder: string;
|
|
4
|
-
/** Support host origin — the sanitiser resolves inline image URLs against it. */
|
|
5
|
-
host: string;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
/** Which controls to show. Defaults to the original's NOTE_TOOLBAR. */
|
|
8
|
-
tools?: EditorTool[];
|
|
9
|
-
/** Hide the toolbar until focused or non-empty. */
|
|
10
|
-
compact?: boolean;
|
|
11
|
-
labels: Record<string, string>;
|
|
12
|
-
/**
|
|
13
|
-
* Store an image and return the URL to reference it by.
|
|
14
|
-
*
|
|
15
|
-
* Optional, and the fallback is not "no images" — it is the annotated data
|
|
16
|
-
* URI carried in the body for the server to store on arrival. A composer on
|
|
17
|
-
* a ticket that does not exist yet has nowhere to upload to, and that is a
|
|
18
|
-
* reason to move the work, not to take the feature away.
|
|
19
|
-
*/
|
|
20
|
-
uploadImage?: (file: File) => Promise<string>;
|
|
21
|
-
/** UI language, for the annotator's own labels. */
|
|
22
|
-
locale?: "th" | "en";
|
|
23
|
-
/** Content to start from — editing an existing message rather than writing one. */
|
|
24
|
-
initial?: string;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Sanitised HTML, or "" when there is nothing worth sending.
|
|
28
|
-
*
|
|
29
|
-
* `cleanHtml` is not belt-and-braces over TipTap's schema. The schema drops an
|
|
30
|
-
* `onclick`, but the Image extension takes any `src` it is given — so a
|
|
31
|
-
* screenshot pasted from a web page arrives with that page's remote image
|
|
32
|
-
* intact, and posting it would store a tracking pixel in the thread aimed at
|
|
33
|
-
* every future reader. The allowlist is what narrows an image to our own.
|
|
34
|
-
*/
|
|
35
|
-
declare function value(): string;
|
|
36
|
-
declare function clear(): void;
|
|
37
|
-
type __VLS_PublicProps = __VLS_Props & {
|
|
38
|
-
/**
|
|
39
|
-
* Two-way binding, for a host that would rather drive this from a ref than call
|
|
40
|
-
* `value()`. Optional: the panel uses the imperative API, the app uses v-model.
|
|
41
|
-
*/
|
|
42
|
-
modelValue?: string;
|
|
43
|
-
};
|
|
44
|
-
declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
45
|
-
value: typeof value;
|
|
46
|
-
clear: typeof clear;
|
|
47
|
-
focus: () => boolean | undefined;
|
|
48
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
49
|
-
"update:modelValue": (value: string | undefined) => any;
|
|
50
|
-
} & {
|
|
51
|
-
error: (message: string) => any;
|
|
52
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
53
|
-
onError?: ((message: string) => any) | undefined;
|
|
54
|
-
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
55
|
-
}>, {
|
|
56
|
-
tools: EditorTool[];
|
|
57
|
-
compact: boolean;
|
|
58
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
59
|
-
export default _default;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { MyTicketDetail, StatusMaps, TicketStrings } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
ticket: MyTicketDetail;
|
|
4
|
-
maps: StatusMaps;
|
|
5
|
-
t: TicketStrings;
|
|
6
|
-
locale: "th" | "en";
|
|
7
|
-
host: string;
|
|
8
|
-
};
|
|
9
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
|
-
export default _default;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A status as the realm coloured it.
|
|
3
|
-
*
|
|
4
|
-
* The tone arrives as DATA — a realm adds lanes whenever it likes — so it is an
|
|
5
|
-
* inline style rather than a class. There is no stylesheet that could know the
|
|
6
|
-
* names in advance.
|
|
7
|
-
*/
|
|
8
|
-
type __VLS_Props = {
|
|
9
|
-
label: string;
|
|
10
|
-
tone: {
|
|
11
|
-
fg: string;
|
|
12
|
-
bg: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
-
export default _default;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
type __VLS_Props = {
|
|
2
|
-
html: string;
|
|
3
|
-
host: string;
|
|
4
|
-
};
|
|
5
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
-
export default _default;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type MyTicketDetail, type TicketStrings } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
ticket: MyTicketDetail;
|
|
4
|
-
t: TicketStrings;
|
|
5
|
-
host: string;
|
|
6
|
-
timezone: string;
|
|
7
|
-
locale: "th" | "en" | undefined;
|
|
8
|
-
sending?: boolean;
|
|
9
|
-
uploadImage: (file: File) => Promise<string>;
|
|
10
|
-
};
|
|
11
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {
|
|
12
|
-
clear: () => void | undefined;
|
|
13
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
14
|
-
error: (message: string) => any;
|
|
15
|
-
edited: () => any;
|
|
16
|
-
send: (body: string) => any;
|
|
17
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
|
-
onError?: ((message: string) => any) | undefined;
|
|
19
|
-
onEdited?: (() => any) | undefined;
|
|
20
|
-
onSend?: ((body: string) => any) | undefined;
|
|
21
|
-
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
|
-
export default _default;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type MyTicketDetail, type RealmConfig, type StatusEvent, type StatusMaps, type TicketStrings } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
ticket: MyTicketDetail;
|
|
4
|
-
history: StatusEvent[];
|
|
5
|
-
config: RealmConfig;
|
|
6
|
-
maps: StatusMaps;
|
|
7
|
-
t: TicketStrings;
|
|
8
|
-
host: string;
|
|
9
|
-
locale: "th" | "en" | undefined;
|
|
10
|
-
sending?: boolean;
|
|
11
|
-
reopening?: boolean;
|
|
12
|
-
uploadImage: (file: File) => Promise<string>;
|
|
13
|
-
attachmentUrl: (id: string) => Promise<string>;
|
|
14
|
-
};
|
|
15
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
16
|
-
error: (message: string) => any;
|
|
17
|
-
back: () => any;
|
|
18
|
-
edited: () => any;
|
|
19
|
-
send: (body: string) => any;
|
|
20
|
-
reopen: (note: string) => any;
|
|
21
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
22
|
-
onError?: ((message: string) => any) | undefined;
|
|
23
|
-
onBack?: (() => any) | undefined;
|
|
24
|
-
onEdited?: (() => any) | undefined;
|
|
25
|
-
onSend?: ((body: string) => any) | undefined;
|
|
26
|
-
onReopen?: ((note: string) => any) | undefined;
|
|
27
|
-
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
|
-
export default _default;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { TicketAttachment } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
images: TicketAttachment[];
|
|
4
|
-
title: string;
|
|
5
|
-
attachmentUrl: (id: string) => Promise<string>;
|
|
6
|
-
};
|
|
7
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
-
export default _default;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { type MyTicketListItem, type Sort, type SortKey, type StatusMaps, type TicketStrings } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
items: MyTicketListItem[];
|
|
4
|
-
maps: StatusMaps;
|
|
5
|
-
t: TicketStrings;
|
|
6
|
-
slug: string | undefined;
|
|
7
|
-
timezone: string;
|
|
8
|
-
locale: "th" | "en" | undefined;
|
|
9
|
-
keyword: string;
|
|
10
|
-
statusFilter: string;
|
|
11
|
-
sort: Sort;
|
|
12
|
-
page: number;
|
|
13
|
-
};
|
|
14
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {
|
|
15
|
-
count: import("vue").ComputedRef<number>;
|
|
16
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
17
|
-
sort: (key: SortKey) => any;
|
|
18
|
-
page: (page: number) => any;
|
|
19
|
-
open: (id: string) => any;
|
|
20
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
21
|
-
onSort?: ((key: SortKey) => any) | undefined;
|
|
22
|
-
onPage?: ((page: number) => any) | undefined;
|
|
23
|
-
onOpen?: ((id: string) => any) | undefined;
|
|
24
|
-
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
25
|
-
export default _default;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type StatusEvent, type StatusMaps } from "@lightworkai.official/debug-capture";
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
history: StatusEvent[];
|
|
4
|
-
status: string;
|
|
5
|
-
createdAt: string;
|
|
6
|
-
maps: StatusMaps;
|
|
7
|
-
timezone: string;
|
|
8
|
-
locale: "th" | "en";
|
|
9
|
-
};
|
|
10
|
-
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
-
export default _default;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The toolbar's icons, as tiny functional components.
|
|
3
|
-
*
|
|
4
|
-
* Drawn rather than imported: the original uses lucide, and a package that
|
|
5
|
-
* pulled an icon set in so a reply box could show a B would cost every consumer
|
|
6
|
-
* that dependency. These are the same lucide paths at the same 24-unit scale.
|
|
7
|
-
*/
|
|
8
|
-
import { type FunctionalComponent } from "vue";
|
|
9
|
-
export declare const BoldIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
10
|
-
export declare const ItalicIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
11
|
-
export declare const UnderlineIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
12
|
-
export declare const BulletListIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
13
|
-
export declare const OrderedListIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
14
|
-
export declare const LinkIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
15
|
-
export declare const ImageIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
16
|
-
export declare const SendIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
17
|
-
export declare const SearchIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
18
|
-
export declare const ArrowLeftIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
19
|
-
export declare const CheckIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
20
|
-
export declare const MessageIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
21
|
-
export declare const RotateIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
22
|
-
export declare const AlertIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
23
|
-
export declare const InboxIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
24
|
-
export declare const ClockIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
25
|
-
export declare const CheckCircleIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
26
|
-
export declare const BanIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
27
|
-
export declare const WrenchIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
28
|
-
export declare const SquareIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
29
|
-
export declare const ArrowUpRightIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
30
|
-
export declare const HighlighterIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
31
|
-
export declare const TypeIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
32
|
-
export declare const TrashIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
33
|
-
export declare const StrikeIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
34
|
-
export declare const CodeIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
35
|
-
export declare const QuoteIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
36
|
-
export declare const PencilIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
37
|
-
export declare const CheckIcon2: FunctionalComponent<{}, {}, any, {}>;
|
|
38
|
-
export declare const XIcon: FunctionalComponent<{}, {}, any, {}>;
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Draw on an image before it is sent.
|
|
4
|
-
*
|
|
5
|
-
* The original opens this the moment you pick a picture, and the reason is in
|
|
6
|
-
* its own comment: annotating BEFORE upload keeps the canvas same-origin, so
|
|
7
|
-
* `toDataURL` is not tainted by a cross-origin presigned URL. Pointing at the
|
|
8
|
-
* problem is also most of what a screenshot is for.
|
|
9
|
-
*
|
|
10
|
-
* The drawing itself is `createAnnotator` from the core — one canvas engine,
|
|
11
|
-
* shared by all three framework packages, because there is no declarative way
|
|
12
|
-
* to draw on a canvas. What is here is only the chrome: a dialog, a toolbar and
|
|
13
|
-
* two buttons.
|
|
14
|
-
*/
|
|
15
|
-
import { onBeforeUnmount, ref, shallowRef, watch } from "vue";
|
|
16
|
-
import {
|
|
17
|
-
createAnnotator,
|
|
18
|
-
getConfig,
|
|
19
|
-
reporterStrings,
|
|
20
|
-
type Annotator,
|
|
21
|
-
type AnnotatorTool,
|
|
22
|
-
type ReporterStrings,
|
|
23
|
-
} from "@lightworkai.official/debug-capture";
|
|
24
|
-
import { ArrowUpRightIcon, HighlighterIcon, SquareIcon, TrashIcon, TypeIcon } from "./icons";
|
|
25
|
-
|
|
26
|
-
const props = defineProps<{
|
|
27
|
-
/**
|
|
28
|
-
* The picked image, as a same-origin data URI. Null closes the dialog, so a
|
|
29
|
-
* caller can keep it mounted and drive it from one nullable ref.
|
|
30
|
-
*/
|
|
31
|
-
src: string | null;
|
|
32
|
-
/** Shown in the description, as the app's dialog does. */
|
|
33
|
-
filename?: string;
|
|
34
|
-
/** Falls back to the configured locale — a caller inside the app need not pass it. */
|
|
35
|
-
locale?: "th" | "en";
|
|
36
|
-
}>();
|
|
37
|
-
|
|
38
|
-
const emit = defineEmits<{
|
|
39
|
-
/** The annotated image, at full resolution. */
|
|
40
|
-
(e: "confirm", dataUri: string): void;
|
|
41
|
-
(e: "cancel"): void;
|
|
42
|
-
}>();
|
|
43
|
-
|
|
44
|
-
const t = reporterStrings(props.locale ?? safeLocale());
|
|
45
|
-
|
|
46
|
-
/** `getConfig()` throws when nothing is configured; the dialog still has words. */
|
|
47
|
-
function safeLocale(): "th" | "en" | undefined {
|
|
48
|
-
try {
|
|
49
|
-
return getConfig().locale;
|
|
50
|
-
} catch {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const TOOLS: { tool: AnnotatorTool; key: keyof ReporterStrings; icon: unknown }[] = [
|
|
55
|
-
{ tool: "rect", key: "toolRect", icon: SquareIcon },
|
|
56
|
-
{ tool: "arrow", key: "toolArrow", icon: ArrowUpRightIcon },
|
|
57
|
-
{ tool: "highlight", key: "toolHighlight", icon: HighlighterIcon },
|
|
58
|
-
{ tool: "text", key: "toolText", icon: TypeIcon },
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
const canvas = ref<HTMLCanvasElement | null>(null);
|
|
62
|
-
// shallowRef: the annotator holds a canvas and its own mutable state, and Vue
|
|
63
|
-
// has no business making any of that reactive.
|
|
64
|
-
const annotator = shallowRef<Annotator | null>(null);
|
|
65
|
-
const active = ref<AnnotatorTool | null>(null);
|
|
66
|
-
const loading = ref(true);
|
|
67
|
-
|
|
68
|
-
watch(
|
|
69
|
-
() => [props.src, canvas.value] as const,
|
|
70
|
-
([src, node]) => {
|
|
71
|
-
if (!src || !node) return;
|
|
72
|
-
annotator.value?.destroy();
|
|
73
|
-
const image = new Image();
|
|
74
|
-
image.addEventListener("load", () => {
|
|
75
|
-
// A backing store at the image's own size, BEFORE the annotator draws
|
|
76
|
-
// into it. Without this the canvas keeps its default 300x150, the image
|
|
77
|
-
// is squashed into that, and CSS then stretches the result — blurry, and
|
|
78
|
-
// the wrong shape.
|
|
79
|
-
node.width = image.naturalWidth;
|
|
80
|
-
node.height = image.naturalHeight;
|
|
81
|
-
annotator.value = createAnnotator(node, image, () => {
|
|
82
|
-
// The toolbar mirrors the engine, so it has to hear about a tool being
|
|
83
|
-
// CONSUMED as well as a shape being added.
|
|
84
|
-
active.value = null;
|
|
85
|
-
});
|
|
86
|
-
loading.value = false;
|
|
87
|
-
});
|
|
88
|
-
image.src = src;
|
|
89
|
-
},
|
|
90
|
-
{ immediate: true },
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
onBeforeUnmount(() => annotator.value?.destroy());
|
|
94
|
-
|
|
95
|
-
function pick(tool: AnnotatorTool): void {
|
|
96
|
-
active.value = active.value === tool ? null : tool;
|
|
97
|
-
annotator.value?.setTool(active.value);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function clearAll(): void {
|
|
101
|
-
annotator.value?.clear();
|
|
102
|
-
active.value = null;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function confirm(): void {
|
|
106
|
-
// Full resolution, never the on-screen scale.
|
|
107
|
-
emit("confirm", annotator.value?.toDataUrl() ?? props.src ?? "");
|
|
108
|
-
}
|
|
109
|
-
</script>
|
|
110
|
-
|
|
111
|
-
<template>
|
|
112
|
-
<div v-if="src" class="lw-annotator" role="dialog" aria-modal="true" @click.self="emit('cancel')">
|
|
113
|
-
<div class="lw-annotator__panel">
|
|
114
|
-
<!-- Header, toolbar, canvas, help, footer — the same shape and the same
|
|
115
|
-
words as the app's own dialog, so the two are one thing in two
|
|
116
|
-
places rather than two things that resemble each other. -->
|
|
117
|
-
<div class="lw-annotator__head">
|
|
118
|
-
<div class="lw-grow">
|
|
119
|
-
<h2 class="lw-annotator__title">{{ t.annotateTitle }}</h2>
|
|
120
|
-
<p class="lw-annotator__desc">
|
|
121
|
-
{{ t.annotateHint }}<template v-if="filename"> · {{ filename }}</template>
|
|
122
|
-
</p>
|
|
123
|
-
</div>
|
|
124
|
-
<button type="button" class="lw-annotator__close" :aria-label="t.cancel" @click="emit('cancel')">✕</button>
|
|
125
|
-
</div>
|
|
126
|
-
|
|
127
|
-
<div class="lw-annotator__tools">
|
|
128
|
-
<button
|
|
129
|
-
v-for="tool in TOOLS"
|
|
130
|
-
:key="tool.tool"
|
|
131
|
-
type="button"
|
|
132
|
-
class="lw-atool"
|
|
133
|
-
:aria-pressed="active === tool.tool"
|
|
134
|
-
:disabled="loading"
|
|
135
|
-
@click="pick(tool.tool)"
|
|
136
|
-
>
|
|
137
|
-
<component :is="tool.icon" /> {{ t[tool.key] }}
|
|
138
|
-
</button>
|
|
139
|
-
<span class="lw-grow" />
|
|
140
|
-
<!-- Right-aligned and on its own, like the app's. Deleting ONE shape is
|
|
141
|
-
the ✕ on the shape, which is what the help line below explains. -->
|
|
142
|
-
<button type="button" class="lw-atool lw-atool--plain" :disabled="loading" @click="clearAll">
|
|
143
|
-
<TrashIcon /> {{ t.clearAll }}
|
|
144
|
-
</button>
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
<div class="lw-annotator__canvas">
|
|
148
|
-
<div class="lw-annotator__canvasbox"><canvas ref="canvas" /></div>
|
|
149
|
-
</div>
|
|
150
|
-
|
|
151
|
-
<p class="lw-annotator__hint">{{ t.annotateHelp }}</p>
|
|
152
|
-
|
|
153
|
-
<div class="lw-annotator__actions">
|
|
154
|
-
<span class="lw-grow" />
|
|
155
|
-
<button type="button" class="lw-btn" @click="emit('cancel')">{{ t.cancel }}</button>
|
|
156
|
-
<button type="button" class="lw-send" :disabled="loading" @click="confirm">{{ t.annotateApply }}</button>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
</template>
|