artarch-feedback-sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -8
- package/dist/widget-labels.d.ts +34 -0
- package/dist/widget-labels.d.ts.map +1 -0
- package/dist/widget-labels.js +43 -0
- package/dist/widget-labels.js.map +1 -0
- package/dist/widget-styles.d.ts +2 -0
- package/dist/widget-styles.d.ts.map +1 -0
- package/dist/widget-styles.js +80 -0
- package/dist/widget-styles.js.map +1 -0
- package/dist/widget-types.d.ts +44 -0
- package/dist/widget-types.d.ts.map +1 -0
- package/dist/widget-types.js +2 -0
- package/dist/widget-types.js.map +1 -0
- package/dist/widget.d.ts +6 -0
- package/dist/widget.d.ts.map +1 -0
- package/dist/widget.js +481 -0
- package/dist/widget.js.map +1 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# artarch-feedback-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Browser SDK for collecting feedback with screenshots and screen recordings. Use the headless core with your own UI, or mount the optional default widget. The SDK owns browser capture and asset lifecycle; the host application owns authentication, CDN storage, and feedback APIs.
|
|
4
4
|
|
|
5
5
|
## About ArtArch
|
|
6
6
|
|
|
@@ -24,6 +24,7 @@ and feedback adapters.
|
|
|
24
24
|
| Capture lifecycle | Handles permission denial, cancellation, overlapping starts/stops, final media chunks, and release of tracks and listeners. |
|
|
25
25
|
| Feedback submission | Accepts text alone, text with a screenshot, text with a recording, or both attachments. Uploads finish before the feedback record is submitted. |
|
|
26
26
|
| Business context | Forwards host-provided `context`, such as platform, project ID, route, or node ID, without collecting it automatically. |
|
|
27
|
+
| Optional default UI | Top-of-page entry, introduction with a persistent opt-out, page frame, recording/screenshot/text toolbar, attachment preview, and feedback form. |
|
|
27
28
|
|
|
28
29
|
Capture requires a secure context (HTTPS or localhost), a user gesture, and the
|
|
29
30
|
browser's permission picker. The available surfaces and audio sources depend on
|
|
@@ -36,10 +37,11 @@ Next.js, Axios, Sentry, or a cloud storage provider.
|
|
|
36
37
|
|
|
37
38
|
| Owner | Responsibilities |
|
|
38
39
|
| --- | --- |
|
|
39
|
-
| SDK | Browser capture, local media files, capture errors and cancellation, and sequencing upload/submission through `FeedbackTransport`. |
|
|
40
|
+
| SDK core | Browser capture, local media files, capture errors and cancellation, and sequencing upload/submission through `FeedbackTransport`. |
|
|
41
|
+
| SDK widget (optional) | Default dialogs, toolbar, styles, text form, local previews, validation, recording timer, and localized capture/submission states. |
|
|
40
42
|
| Host `uploadAsset` adapter | Authentication, upload credentials, storage/CDN selection, and uploading the provided file. Returns `{ url, id? }`. |
|
|
41
43
|
| Host `submitFeedback` adapter | Sending the message, uploaded attachment URLs, and context to a business API. Returns `{ id? }`. |
|
|
42
|
-
| Host application |
|
|
44
|
+
| Host application | Transport adapters, context selection, retention, and the feedback management backend. It may customize the default widget or replace it with its own UI using the core API. |
|
|
43
45
|
|
|
44
46
|
A host can build this user flow around the SDK:
|
|
45
47
|
|
|
@@ -55,14 +57,18 @@ uploaded files left behind when another upload or the final submission fails.
|
|
|
55
57
|
|
|
56
58
|
## Current scope
|
|
57
59
|
|
|
58
|
-
The
|
|
59
|
-
|
|
60
|
+
The SDK includes a headless core and an optional default widget. It does not
|
|
61
|
+
include screenshot annotation/redaction, video editing, a feedback backend,
|
|
60
62
|
or an administration console. It does not automatically collect console logs,
|
|
61
63
|
network requests, page URLs, or historical session replays.
|
|
62
64
|
|
|
63
65
|
The npm package name is `artarch-feedback-sdk`; its directory in this repository
|
|
64
|
-
is `packages/feedback-sdk`.
|
|
65
|
-
`
|
|
66
|
+
is `packages/feedback-sdk`. Version `0.1.0` contains the core API; version `0.2.0`
|
|
67
|
+
adds the optional `/widget` entry with the default UI and customization options.
|
|
68
|
+
The tool-canvas integration mounts it globally, but its API
|
|
69
|
+
adapters are deliberately unconfigured: submission keeps the draft and displays
|
|
70
|
+
an unavailable message, without uploading or claiming success. Neither this UI
|
|
71
|
+
integration nor its transport is deployed to production.
|
|
66
72
|
|
|
67
73
|
## Install
|
|
68
74
|
|
|
@@ -74,6 +80,89 @@ pnpm add artarch-feedback-sdk
|
|
|
74
80
|
|
|
75
81
|
For local development, run `npm pack` in this package and install the
|
|
76
82
|
resulting `.tgz` in a consuming project. See the build and test commands below.
|
|
83
|
+
Workspace installation builds the SDK through its `prepare` script. If install
|
|
84
|
+
scripts are disabled, run `pnpm --filter artarch-feedback-sdk build` before
|
|
85
|
+
building or starting tool-canvas; rebuild it after changing SDK source.
|
|
86
|
+
|
|
87
|
+
## Default widget
|
|
88
|
+
|
|
89
|
+
The optional UI is a separate entry, with no React or CSS framework dependency.
|
|
90
|
+
Import it only when you want the built-in interface. Call it after the document
|
|
91
|
+
is ready (for example, in a React effect), and call `destroy()` on unmount.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { createFeedbackWidget } from 'artarch-feedback-sdk/widget';
|
|
95
|
+
|
|
96
|
+
const widget = createFeedbackWidget({
|
|
97
|
+
transport: { uploadAsset, submitFeedback }, // Your adapters; see below.
|
|
98
|
+
locale: 'zh', // 'en' by default
|
|
99
|
+
context: () => ({ projectId, route: window.location.pathname }),
|
|
100
|
+
recording: { maxDurationMs: 60_000, audio: false },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// On application unmount:
|
|
104
|
+
widget.destroy();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The default entry sits at the top center of the page. Opening it shows a page
|
|
108
|
+
frame and a toolbar with Record, Screenshot, and Text only. The introduction
|
|
109
|
+
appears once per widget instance; checking "Don't show this again" persists the
|
|
110
|
+
preference in localStorage. Storage being unavailable does not block feedback.
|
|
111
|
+
|
|
112
|
+
- Text opens a compact form beneath the toolbar. Blank/whitespace-only messages
|
|
113
|
+
cannot be sent; descriptions are limited to 5,000 characters.
|
|
114
|
+
- Screenshot capture hides the widget before opening the browser picker, then
|
|
115
|
+
displays the resulting image in a preview dialog with the description form.
|
|
116
|
+
- Recording keeps the page interactive and shows a timer, Stop, and Close.
|
|
117
|
+
Manual stop, the duration limit, and browser-ended sharing all open a video
|
|
118
|
+
preview. Close cancels and discards capture. The toolbar may appear in a recording.
|
|
119
|
+
- The paperclip accepts one PNG, JPEG, WebP, GIF, MP4, or WebM attachment, up to
|
|
120
|
+
50 MiB by default. A new attachment replaces the previous one; remove returns
|
|
121
|
+
to text feedback without clearing the message. The core API can still accept
|
|
122
|
+
both a screenshot and a recording in a single submission.
|
|
123
|
+
- Files stay local until Send. Failed submissions retain the message and preview
|
|
124
|
+
for retry. Closing cancels pending work and revokes local preview URLs. Drafts
|
|
125
|
+
are in memory only; closing, navigating away, or reloading discards them.
|
|
126
|
+
|
|
127
|
+
### Customize or replace the UI
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const widget = createFeedbackWidget({
|
|
131
|
+
transport: { uploadAsset, submitFeedback },
|
|
132
|
+
trigger: false, // Use your own entry button.
|
|
133
|
+
introduction: { storageKey: 'my-app:feedback-intro' }, // Or false to skip it.
|
|
134
|
+
locale: 'en',
|
|
135
|
+
labels: { trigger: 'Report a problem', introTitle: 'Help us improve' },
|
|
136
|
+
theme: {
|
|
137
|
+
'--feedback-accent': '#e0ff64',
|
|
138
|
+
'--feedback-accent-text': '#171717',
|
|
139
|
+
'--feedback-surface': '#141414',
|
|
140
|
+
'--feedback-top': '12px',
|
|
141
|
+
},
|
|
142
|
+
styles: '.text-panel { border-radius: 16px; }',
|
|
143
|
+
formatSubmitError: () => 'Could not send. Please try again.',
|
|
144
|
+
onSuccess: result => console.log('Feedback accepted:', result.id),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
document.querySelector('#my-feedback-button')?.addEventListener('click', widget.open);
|
|
148
|
+
widget.setLocale('zh'); // Updates labels without losing the draft.
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Shadow DOM isolates the widget from host styles. Override `theme` custom properties
|
|
152
|
+
or use trusted `styles` inside the shadow root; external CSS can also target
|
|
153
|
+
`[data-artarch-feedback]::part(...)` or a class you assign to `widget.element`.
|
|
154
|
+
Public parts are `trigger`,
|
|
155
|
+
`frame`, `toolbar`, `intro`, `text-panel`, `review`, `preview`, `form`, `textarea`,
|
|
156
|
+
`footer`, and `notice`. Other variables include `--feedback-text`,
|
|
157
|
+
`--feedback-muted`, `--feedback-border`, `--feedback-field`, `--feedback-focus`,
|
|
158
|
+
`--feedback-error`, `--feedback-radius`, `--feedback-font`, and
|
|
159
|
+
`--feedback-z-index`. `nonce` supports applications with a CSP style nonce.
|
|
160
|
+
Native dialogs provide modal focus handling; the compact text panel leaves the
|
|
161
|
+
page usable. The layout adapts to narrow viewports and respects reduced motion.
|
|
162
|
+
|
|
163
|
+
For fully custom dialogs or rendering, use `FeedbackClient` from the main entry
|
|
164
|
+
and build your own interface. It does not import or mount the widget. The host
|
|
165
|
+
retains complete control of its upload and submission adapters in either mode.
|
|
77
166
|
|
|
78
167
|
## Host-provided transport
|
|
79
168
|
|
|
@@ -169,6 +258,7 @@ From the workspace root:
|
|
|
169
258
|
|
|
170
259
|
```bash
|
|
171
260
|
pnpm install --frozen-lockfile
|
|
261
|
+
pnpm --filter artarch-feedback-sdk build
|
|
172
262
|
pnpm --filter artarch-feedback-sdk exec playwright install chromium
|
|
173
263
|
pnpm --filter artarch-feedback-sdk test:all
|
|
174
264
|
```
|
|
@@ -188,9 +278,30 @@ npm run test:all # Both suites
|
|
|
188
278
|
outside the repository and checks native Node ESM imports and NodeNext types.
|
|
189
279
|
- Playwright serves a local fixture on a random port and closes it after testing.
|
|
190
280
|
It uses native `getDisplayMedia` and `MediaRecorder`, with Chromium test flags
|
|
191
|
-
selecting
|
|
281
|
+
selecting the fixture tab and Permissions Policy producing native capture
|
|
282
|
+
denial. Tests decode PNG pixels at desktop and
|
|
192
283
|
narrow viewport sizes, play and sample changing video frames, and check cleanup.
|
|
193
284
|
No production accounts, CDN endpoints or feedback services are contacted.
|
|
285
|
+
- Widget tests cover introduction preferences, text validation, real screenshot
|
|
286
|
+
and video previews, automatic stop, failure/retry, cancellation, late capture
|
|
287
|
+
cleanup, attachment limits, localization, custom styles, and narrow viewports.
|
|
288
|
+
Transport callbacks are test doubles; they do not establish backend integration.
|
|
289
|
+
|
|
290
|
+
The host integration has separate checks:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# From the tool-canvas workspace root:
|
|
294
|
+
node --experimental-strip-types --test src/components/Feedback/transport.test.ts
|
|
295
|
+
pnpm build:studio-test
|
|
296
|
+
pnpm exec dotenv -e .env.studio.test -- next start -p 5574
|
|
297
|
+
# In another terminal, against that local app:
|
|
298
|
+
node scripts/verify-feedback-ui.mjs
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The page check covers English and Chinese, the global entry, introduction,
|
|
302
|
+
unconfigured submission, and draft retention. It blocks remote requests and
|
|
303
|
+
saves screenshots under this package's `test-results/app-smoke/`. Override
|
|
304
|
+
`FEEDBACK_TEST_BASE_URL` for a different localhost port or base path.
|
|
194
305
|
|
|
195
306
|
Successful screenshots and recordings are attached under `test-results/`.
|
|
196
307
|
Failed browser tests retain screenshots and traces. Browser binaries are test
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface FeedbackWidgetLabels {
|
|
2
|
+
trigger: string;
|
|
3
|
+
toolbar: string;
|
|
4
|
+
record: string;
|
|
5
|
+
screenshot: string;
|
|
6
|
+
text: string;
|
|
7
|
+
close: string;
|
|
8
|
+
introTitle: string;
|
|
9
|
+
introBody: string;
|
|
10
|
+
dontShowAgain: string;
|
|
11
|
+
acknowledge: string;
|
|
12
|
+
description: string;
|
|
13
|
+
placeholder: string;
|
|
14
|
+
attach: string;
|
|
15
|
+
removeAttachment: string;
|
|
16
|
+
cancel: string;
|
|
17
|
+
send: string;
|
|
18
|
+
sending: string;
|
|
19
|
+
stop: string;
|
|
20
|
+
requesting: string;
|
|
21
|
+
finalizing: string;
|
|
22
|
+
recording: string;
|
|
23
|
+
screenshotPreview: string;
|
|
24
|
+
recordingPreview: string;
|
|
25
|
+
success: string;
|
|
26
|
+
submitFailed: string;
|
|
27
|
+
captureFailed: string;
|
|
28
|
+
permissionDenied: string;
|
|
29
|
+
unsupported: string;
|
|
30
|
+
invalidAttachment: string;
|
|
31
|
+
attachmentTooLarge: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function resolveWidgetLabels(locale: 'en' | 'zh', overrides?: Partial<FeedbackWidgetLabels>): FeedbackWidgetLabels;
|
|
34
|
+
//# sourceMappingURL=widget-labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-labels.d.ts","sourceRoot":"","sources":["../src/widget-labels.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AA2CD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,oBAAoB,CAExH"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const en = {
|
|
2
|
+
trigger: 'Send feedback', toolbar: 'Feedback tools', record: 'Record',
|
|
3
|
+
screenshot: 'Screenshot', text: 'Text only', close: 'Close feedback',
|
|
4
|
+
introTitle: 'Thanks for helping us improve!',
|
|
5
|
+
introBody: 'Use the toolbar at the top of the page to record your screen, take a screenshot, or just send a message. Visual context helps us understand what happened. We may not reply to every report, but our team reads them all.',
|
|
6
|
+
dontShowAgain: "Don't show this again", acknowledge: 'Got it',
|
|
7
|
+
description: 'Describe your feedback',
|
|
8
|
+
placeholder: 'Describe what happened and what you expected to see.',
|
|
9
|
+
attach: 'Attach an image or video', removeAttachment: 'Remove attachment',
|
|
10
|
+
cancel: 'Cancel', send: 'Send feedback', sending: 'Sending…',
|
|
11
|
+
stop: 'Stop recording', requesting: 'Choose a screen to share…',
|
|
12
|
+
finalizing: 'Preparing recording…', recording: 'Recording',
|
|
13
|
+
screenshotPreview: 'Screenshot preview', recordingPreview: 'Recording preview',
|
|
14
|
+
success: 'Thank you! Your feedback has been sent.',
|
|
15
|
+
submitFailed: "Couldn't send your feedback. Your draft is still here; please try again.",
|
|
16
|
+
captureFailed: "Couldn't capture your screen. Please try again or send text feedback.",
|
|
17
|
+
permissionDenied: 'Screen sharing was not allowed. Please try again or send text feedback.',
|
|
18
|
+
unsupported: 'Screen capture is unavailable in this browser. You can still send text or attach a file.',
|
|
19
|
+
invalidAttachment: 'Choose a PNG, JPEG, WebP, GIF, MP4, or WebM file.',
|
|
20
|
+
attachmentTooLarge: 'This attachment is too large. Choose a smaller file or make a shorter recording.',
|
|
21
|
+
};
|
|
22
|
+
const zh = {
|
|
23
|
+
trigger: '发送反馈', toolbar: '反馈工具栏', record: '录屏', screenshot: '截图',
|
|
24
|
+
text: '纯文字', close: '关闭反馈', introTitle: '感谢您帮助我们改进!',
|
|
25
|
+
introBody: '请使用页面顶部中央的工具栏。您可以录屏、截图,也可以只发送文字,其中录屏最能帮助我们了解问题细节。我们不会逐条回复,但每一条团队都会看到。',
|
|
26
|
+
dontShowAgain: '以后不再提示', acknowledge: '好的', description: '反馈描述',
|
|
27
|
+
placeholder: '请描述发生了什么,以及您原本期望看到什么。',
|
|
28
|
+
attach: '添加图片或视频附件', removeAttachment: '移除附件', cancel: '取消',
|
|
29
|
+
send: '发送反馈', sending: '正在发送…', stop: '停止录屏',
|
|
30
|
+
requesting: '请选择要共享的屏幕…', finalizing: '正在生成录屏…', recording: '录屏中',
|
|
31
|
+
screenshotPreview: '截图预览', recordingPreview: '录屏预览',
|
|
32
|
+
success: '感谢您的反馈,已发送给团队。',
|
|
33
|
+
submitFailed: '反馈发送失败,内容已保留,请重试。',
|
|
34
|
+
captureFailed: '屏幕捕获失败,请重试或使用纯文字反馈。',
|
|
35
|
+
permissionDenied: '未获得屏幕共享权限,请重试或使用纯文字反馈。',
|
|
36
|
+
unsupported: '当前浏览器不支持屏幕捕获,您仍可发送文字或添加附件。',
|
|
37
|
+
invalidAttachment: '请选择 PNG、JPEG、WebP、GIF、MP4 或 WebM 文件。',
|
|
38
|
+
attachmentTooLarge: '附件过大,请选择更小的文件或缩短录屏时间。',
|
|
39
|
+
};
|
|
40
|
+
export function resolveWidgetLabels(locale, overrides) {
|
|
41
|
+
return { ...(locale === 'zh' ? zh : en), ...overrides };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=widget-labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-labels.js","sourceRoot":"","sources":["../src/widget-labels.ts"],"names":[],"mappings":"AAiCA,MAAM,EAAE,GAAyB;IAC/B,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ;IACrE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB;IACpE,UAAU,EAAE,gCAAgC;IAC5C,SAAS,EAAE,2NAA2N;IACtO,aAAa,EAAE,uBAAuB,EAAE,WAAW,EAAE,QAAQ;IAC7D,WAAW,EAAE,wBAAwB;IACrC,WAAW,EAAE,sDAAsD;IACnE,MAAM,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,mBAAmB;IACzE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,UAAU;IAC5D,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,2BAA2B;IAC/D,UAAU,EAAE,sBAAsB,EAAE,SAAS,EAAE,WAAW;IAC1D,iBAAiB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,mBAAmB;IAC9E,OAAO,EAAE,yCAAyC;IAClD,YAAY,EAAE,0EAA0E;IACxF,aAAa,EAAE,uEAAuE;IACtF,gBAAgB,EAAE,yEAAyE;IAC3F,WAAW,EAAE,0FAA0F;IACvG,iBAAiB,EAAE,mDAAmD;IACtE,kBAAkB,EAAE,kFAAkF;CACvG,CAAC;AAEF,MAAM,EAAE,GAAyB;IAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI;IACjE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY;IACpD,SAAS,EAAE,uEAAuE;IAClF,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM;IAC/D,WAAW,EAAE,uBAAuB;IACpC,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI;IAC3D,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM;IAC5C,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK;IACjE,iBAAiB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IACnD,OAAO,EAAE,gBAAgB;IACzB,YAAY,EAAE,mBAAmB;IACjC,aAAa,EAAE,qBAAqB;IACpC,gBAAgB,EAAE,wBAAwB;IAC1C,WAAW,EAAE,4BAA4B;IACzC,iBAAiB,EAAE,sCAAsC;IACzD,kBAAkB,EAAE,uBAAuB;CAC5C,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,MAAmB,EAAE,SAAyC;IAChG,OAAO,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const widgetStyles = "\n:host {\n --feedback-surface: #0b0b0b;\n --feedback-field: #101010;\n --feedback-border: #303030;\n --feedback-text: #f4f4f4;\n --feedback-muted: #a0a0a0;\n --feedback-accent: #ededed;\n --feedback-accent-text: #161616;\n --feedback-focus: #b6b6b6;\n --feedback-error: #ff9999;\n --feedback-radius: 12px;\n --feedback-top: 8px;\n --feedback-z-index: 10000;\n font-family: var(--feedback-font, ui-sans-serif, system-ui, sans-serif);\n font-size: 14px;\n line-height: 1.6;\n color: var(--feedback-text);\n color-scheme: dark;\n}\n*, *::before, *::after { box-sizing: border-box; }\n[hidden] { display: none !important; }\nbutton, textarea, input { font: inherit; }\nbutton { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 34px; padding: 6px 12px; border: 1px solid var(--feedback-border); border-radius: 7px; background: transparent; color: inherit; cursor: pointer; white-space: nowrap; transition: background 120ms, color 120ms; }\nbutton:hover:not(:disabled) { background: #ffffff12; }\nbutton:disabled { opacity: .4; cursor: not-allowed; }\nbutton:focus-visible, input:focus-visible { outline: 2px solid var(--feedback-focus); outline-offset: 3px; }\nsvg { width: 16px; height: 16px; flex-shrink: 0; }\n.primary, button[aria-pressed=\"true\"] { background: var(--feedback-accent); color: var(--feedback-accent-text); border-color: transparent; }\n.primary:hover:not(:disabled), button[aria-pressed=\"true\"]:hover:not(:disabled) { background: var(--feedback-accent); filter: brightness(.92); }\n.icon-button { width: 34px; padding: 6px; flex-shrink: 0; border-color: transparent; }\n.entry, .toolbar, .text-panel, .notice { position: fixed; left: 50%; transform: translateX(-50%); z-index: var(--feedback-z-index); }\n.entry { top: var(--feedback-top); min-height: 32px; padding: 5px 12px; border-radius: 9px; background: var(--feedback-surface); box-shadow: 0 2px 10px #0002; font-size: 12px; }\n.frame { position: fixed; inset: 3px; border: 2px solid var(--feedback-accent); border-radius: 14px; pointer-events: none; z-index: calc(var(--feedback-z-index) - 1); box-shadow: 0 0 0 4px #0006; }\n.toolbar { top: var(--feedback-top); display: flex; align-items: center; gap: 8px; max-width: calc(100vw - 24px); padding: 7px; border: 1px solid var(--feedback-border); border-radius: 13px; background: var(--feedback-surface); box-shadow: 0 10px 35px #0004; }\n.modes { display: flex; align-items: center; gap: 2px; padding: 3px; border: 1px solid var(--feedback-border); border-radius: 8px; background: #ffffff08; }\n.modes button { border: 0; font-size: 12px; min-height: 30px; padding: 5px 9px; }\n.toolbar > .icon-button { border-left: 1px solid var(--feedback-border); border-radius: 0; }\n.recording-controls { display: flex; align-items: center; gap: 10px; padding-left: 5px; }\n.timer { font-variant-numeric: tabular-nums; white-space: nowrap; font-size: 13px; }\n.recording-dot { width: 7px; height: 7px; border-radius: 50%; background: #ff6464; animation: feedback-pulse 1.5s infinite; }\n.text-panel { top: calc(var(--feedback-top) + 64px); width: min(448px, calc(100vw - 24px)); padding: 10px; border: 1px solid var(--feedback-border); border-radius: 16px; background: var(--feedback-surface); box-shadow: 0 16px 60px #0005; }\ndialog { color: var(--feedback-text); background: var(--feedback-surface); border: 1px solid var(--feedback-border); border-radius: var(--feedback-radius); padding: 24px; box-shadow: 0 24px 100px #0008; max-width: calc(100vw - 32px); max-height: calc(100dvh - 40px); overflow: auto; font: inherit; }\ndialog::backdrop { background: #0008; }\n.intro { width: 448px; margin: 0 auto; top: calc(var(--feedback-top) + 64px); }\n.intro::backdrop { background: #0004; }\nh2 { margin: 0; font-size: 18px; line-height: 1.4; font-weight: 600; letter-spacing: -.02em; }\n.intro p { margin: 8px 0 20px; color: var(--feedback-muted); font-size: 14px; line-height: 1.75; }\n.intro-actions { display: flex; justify-content: space-between; align-items: center; gap: 12px; }\n.intro label { display: flex; gap: 8px; align-items: center; font-size: 13px; cursor: pointer; }\n.intro input { width: 16px; height: 16px; margin: 0; accent-color: var(--feedback-accent); }\n.review { width: 768px; }\n.review-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }\n.preview { position: relative; border: 1px solid var(--feedback-border); border-radius: 8px; overflow: hidden; background: #060606; margin-bottom: 16px; }\n.preview img, .preview video { display: block; width: 100%; max-height: min(50dvh, 430px); object-fit: contain; }\n.remove { position: absolute; right: 8px; top: 8px; background: #0b0b0be6; color: #fff; border: 1px solid #ffffff30; border-radius: 8px; }\n.filename { display: block; padding: 5px 10px; border-top: 1px solid var(--feedback-border); color: var(--feedback-muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\ntextarea { display: block; width: 100%; min-height: 132px; max-height: 40dvh; resize: vertical; padding: 12px; background: var(--feedback-field); color: var(--feedback-text); border: 1px solid var(--feedback-border); border-radius: 10px; font-size: 14px; line-height: 1.65; }\ntextarea::placeholder { color: var(--feedback-muted); opacity: 1; }\ntextarea:focus { outline: 2px solid var(--feedback-focus); outline-offset: 2px; }\n.review textarea { min-height: 64px; border-radius: 7px; }\n.footer { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 14px; }\n.footer .attach { margin-right: auto; }\n.text-panel .footer { margin-top: 10px; }\n.error { color: var(--feedback-error); font-size: 13px; margin: 10px 2px 0; }\n.notice { top: calc(var(--feedback-top) + 68px); max-width: min(460px, calc(100vw - 24px)); display: flex; align-items: center; gap: 10px; padding: 12px 14px; border: 1px solid var(--feedback-border); border-radius: 10px; background: var(--feedback-surface); box-shadow: 0 12px 36px #0005; }\n.notice[data-error=\"true\"] { color: var(--feedback-error); }\n.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }\n@keyframes feedback-pulse { 50% { opacity: .4; } }\n@media (max-width: 480px) {\n .toolbar { gap: 4px; }\n .modes button { padding: 5px 7px; }\n dialog { padding: 18px; max-width: calc(100vw - 24px); }\n .review { max-height: calc(100dvh - 24px); }\n .preview img, .preview video { max-height: 38dvh; }\n .footer button { padding-left: 10px; padding-right: 10px; }\n}\n@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }\n";
|
|
2
|
+
//# sourceMappingURL=widget-styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-styles.d.ts","sourceRoot":"","sources":["../src/widget-styles.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,6oNA8ExB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const widgetStyles = `
|
|
2
|
+
:host {
|
|
3
|
+
--feedback-surface: #0b0b0b;
|
|
4
|
+
--feedback-field: #101010;
|
|
5
|
+
--feedback-border: #303030;
|
|
6
|
+
--feedback-text: #f4f4f4;
|
|
7
|
+
--feedback-muted: #a0a0a0;
|
|
8
|
+
--feedback-accent: #ededed;
|
|
9
|
+
--feedback-accent-text: #161616;
|
|
10
|
+
--feedback-focus: #b6b6b6;
|
|
11
|
+
--feedback-error: #ff9999;
|
|
12
|
+
--feedback-radius: 12px;
|
|
13
|
+
--feedback-top: 8px;
|
|
14
|
+
--feedback-z-index: 10000;
|
|
15
|
+
font-family: var(--feedback-font, ui-sans-serif, system-ui, sans-serif);
|
|
16
|
+
font-size: 14px;
|
|
17
|
+
line-height: 1.6;
|
|
18
|
+
color: var(--feedback-text);
|
|
19
|
+
color-scheme: dark;
|
|
20
|
+
}
|
|
21
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
22
|
+
[hidden] { display: none !important; }
|
|
23
|
+
button, textarea, input { font: inherit; }
|
|
24
|
+
button { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 34px; padding: 6px 12px; border: 1px solid var(--feedback-border); border-radius: 7px; background: transparent; color: inherit; cursor: pointer; white-space: nowrap; transition: background 120ms, color 120ms; }
|
|
25
|
+
button:hover:not(:disabled) { background: #ffffff12; }
|
|
26
|
+
button:disabled { opacity: .4; cursor: not-allowed; }
|
|
27
|
+
button:focus-visible, input:focus-visible { outline: 2px solid var(--feedback-focus); outline-offset: 3px; }
|
|
28
|
+
svg { width: 16px; height: 16px; flex-shrink: 0; }
|
|
29
|
+
.primary, button[aria-pressed="true"] { background: var(--feedback-accent); color: var(--feedback-accent-text); border-color: transparent; }
|
|
30
|
+
.primary:hover:not(:disabled), button[aria-pressed="true"]:hover:not(:disabled) { background: var(--feedback-accent); filter: brightness(.92); }
|
|
31
|
+
.icon-button { width: 34px; padding: 6px; flex-shrink: 0; border-color: transparent; }
|
|
32
|
+
.entry, .toolbar, .text-panel, .notice { position: fixed; left: 50%; transform: translateX(-50%); z-index: var(--feedback-z-index); }
|
|
33
|
+
.entry { top: var(--feedback-top); min-height: 32px; padding: 5px 12px; border-radius: 9px; background: var(--feedback-surface); box-shadow: 0 2px 10px #0002; font-size: 12px; }
|
|
34
|
+
.frame { position: fixed; inset: 3px; border: 2px solid var(--feedback-accent); border-radius: 14px; pointer-events: none; z-index: calc(var(--feedback-z-index) - 1); box-shadow: 0 0 0 4px #0006; }
|
|
35
|
+
.toolbar { top: var(--feedback-top); display: flex; align-items: center; gap: 8px; max-width: calc(100vw - 24px); padding: 7px; border: 1px solid var(--feedback-border); border-radius: 13px; background: var(--feedback-surface); box-shadow: 0 10px 35px #0004; }
|
|
36
|
+
.modes { display: flex; align-items: center; gap: 2px; padding: 3px; border: 1px solid var(--feedback-border); border-radius: 8px; background: #ffffff08; }
|
|
37
|
+
.modes button { border: 0; font-size: 12px; min-height: 30px; padding: 5px 9px; }
|
|
38
|
+
.toolbar > .icon-button { border-left: 1px solid var(--feedback-border); border-radius: 0; }
|
|
39
|
+
.recording-controls { display: flex; align-items: center; gap: 10px; padding-left: 5px; }
|
|
40
|
+
.timer { font-variant-numeric: tabular-nums; white-space: nowrap; font-size: 13px; }
|
|
41
|
+
.recording-dot { width: 7px; height: 7px; border-radius: 50%; background: #ff6464; animation: feedback-pulse 1.5s infinite; }
|
|
42
|
+
.text-panel { top: calc(var(--feedback-top) + 64px); width: min(448px, calc(100vw - 24px)); padding: 10px; border: 1px solid var(--feedback-border); border-radius: 16px; background: var(--feedback-surface); box-shadow: 0 16px 60px #0005; }
|
|
43
|
+
dialog { color: var(--feedback-text); background: var(--feedback-surface); border: 1px solid var(--feedback-border); border-radius: var(--feedback-radius); padding: 24px; box-shadow: 0 24px 100px #0008; max-width: calc(100vw - 32px); max-height: calc(100dvh - 40px); overflow: auto; font: inherit; }
|
|
44
|
+
dialog::backdrop { background: #0008; }
|
|
45
|
+
.intro { width: 448px; margin: 0 auto; top: calc(var(--feedback-top) + 64px); }
|
|
46
|
+
.intro::backdrop { background: #0004; }
|
|
47
|
+
h2 { margin: 0; font-size: 18px; line-height: 1.4; font-weight: 600; letter-spacing: -.02em; }
|
|
48
|
+
.intro p { margin: 8px 0 20px; color: var(--feedback-muted); font-size: 14px; line-height: 1.75; }
|
|
49
|
+
.intro-actions { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
|
|
50
|
+
.intro label { display: flex; gap: 8px; align-items: center; font-size: 13px; cursor: pointer; }
|
|
51
|
+
.intro input { width: 16px; height: 16px; margin: 0; accent-color: var(--feedback-accent); }
|
|
52
|
+
.review { width: 768px; }
|
|
53
|
+
.review-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
|
|
54
|
+
.preview { position: relative; border: 1px solid var(--feedback-border); border-radius: 8px; overflow: hidden; background: #060606; margin-bottom: 16px; }
|
|
55
|
+
.preview img, .preview video { display: block; width: 100%; max-height: min(50dvh, 430px); object-fit: contain; }
|
|
56
|
+
.remove { position: absolute; right: 8px; top: 8px; background: #0b0b0be6; color: #fff; border: 1px solid #ffffff30; border-radius: 8px; }
|
|
57
|
+
.filename { display: block; padding: 5px 10px; border-top: 1px solid var(--feedback-border); color: var(--feedback-muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
58
|
+
textarea { display: block; width: 100%; min-height: 132px; max-height: 40dvh; resize: vertical; padding: 12px; background: var(--feedback-field); color: var(--feedback-text); border: 1px solid var(--feedback-border); border-radius: 10px; font-size: 14px; line-height: 1.65; }
|
|
59
|
+
textarea::placeholder { color: var(--feedback-muted); opacity: 1; }
|
|
60
|
+
textarea:focus { outline: 2px solid var(--feedback-focus); outline-offset: 2px; }
|
|
61
|
+
.review textarea { min-height: 64px; border-radius: 7px; }
|
|
62
|
+
.footer { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 14px; }
|
|
63
|
+
.footer .attach { margin-right: auto; }
|
|
64
|
+
.text-panel .footer { margin-top: 10px; }
|
|
65
|
+
.error { color: var(--feedback-error); font-size: 13px; margin: 10px 2px 0; }
|
|
66
|
+
.notice { top: calc(var(--feedback-top) + 68px); max-width: min(460px, calc(100vw - 24px)); display: flex; align-items: center; gap: 10px; padding: 12px 14px; border: 1px solid var(--feedback-border); border-radius: 10px; background: var(--feedback-surface); box-shadow: 0 12px 36px #0005; }
|
|
67
|
+
.notice[data-error="true"] { color: var(--feedback-error); }
|
|
68
|
+
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
69
|
+
@keyframes feedback-pulse { 50% { opacity: .4; } }
|
|
70
|
+
@media (max-width: 480px) {
|
|
71
|
+
.toolbar { gap: 4px; }
|
|
72
|
+
.modes button { padding: 5px 7px; }
|
|
73
|
+
dialog { padding: 18px; max-width: calc(100vw - 24px); }
|
|
74
|
+
.review { max-height: calc(100dvh - 24px); }
|
|
75
|
+
.preview img, .preview video { max-height: 38dvh; }
|
|
76
|
+
.footer button { padding-left: 10px; padding-right: 10px; }
|
|
77
|
+
}
|
|
78
|
+
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
|
|
79
|
+
`;
|
|
80
|
+
//# sourceMappingURL=widget-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-styles.js","sourceRoot":"","sources":["../src/widget-styles.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8E3B,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { FeedbackTransport, RecordingOptions, ScreenshotOptions } from './types.js';
|
|
2
|
+
import type { FeedbackWidgetLabels } from './widget-labels.js';
|
|
3
|
+
export interface FeedbackWidgetOptions {
|
|
4
|
+
transport: FeedbackTransport;
|
|
5
|
+
/** Read when the user submits; the SDK does not collect page or user data. */
|
|
6
|
+
context?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
7
|
+
locale?: 'en' | 'zh';
|
|
8
|
+
labels?: Partial<FeedbackWidgetLabels>;
|
|
9
|
+
/** false hides the built-in entry button. Call open() from your own button. */
|
|
10
|
+
trigger?: boolean;
|
|
11
|
+
/** Defaults to document.body. Use a container in the main document. */
|
|
12
|
+
container?: HTMLElement;
|
|
13
|
+
/** false skips the introduction. The string is its localStorage preference key. */
|
|
14
|
+
introduction?: false | {
|
|
15
|
+
storageKey?: string;
|
|
16
|
+
};
|
|
17
|
+
screenshot?: Omit<ScreenshotOptions, 'signal'>;
|
|
18
|
+
/** Defaults to 60 seconds, without audio. */
|
|
19
|
+
recording?: Omit<RecordingOptions, 'signal'>;
|
|
20
|
+
/** Maximum size of a captured or attached file, in bytes. Defaults to 50 MiB. */
|
|
21
|
+
maxAttachmentBytes?: number;
|
|
22
|
+
/** CSS custom properties applied to the widget host, e.g. --feedback-surface. */
|
|
23
|
+
theme?: Record<`--feedback-${string}`, string>;
|
|
24
|
+
/** Trusted CSS appended inside the Shadow DOM; never pass user input here. */
|
|
25
|
+
styles?: string;
|
|
26
|
+
/** CSP nonce for the widget's style element. */
|
|
27
|
+
nonce?: string;
|
|
28
|
+
onSuccess?: (result: {
|
|
29
|
+
id?: string;
|
|
30
|
+
}) => void;
|
|
31
|
+
onClose?: () => void;
|
|
32
|
+
/** Map transport errors to a safe, localized message. Raw errors are not displayed. */
|
|
33
|
+
formatSubmitError?: (error: unknown) => string;
|
|
34
|
+
}
|
|
35
|
+
export interface FeedbackWidget {
|
|
36
|
+
readonly element: HTMLElement;
|
|
37
|
+
open(): void;
|
|
38
|
+
/** Discard the draft and cancel any pending capture or submission. */
|
|
39
|
+
close(): void;
|
|
40
|
+
/** Removes all UI/listeners, revokes previews and releases capture. Safe to repeat. */
|
|
41
|
+
destroy(): void;
|
|
42
|
+
setLocale(locale: 'en' | 'zh', labels?: Partial<FeedbackWidgetLabels>): void;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=widget-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-types.d.ts","sourceRoot":"","sources":["../src/widget-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uEAAuE;IACvE,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,mFAAmF;IACnF,YAAY,CAAC,EAAE,KAAK,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,UAAU,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;IAC/C,6CAA6C;IAC7C,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IAC7C,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/C,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uFAAuF;IACvF,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,IAAI,IAAI,IAAI,CAAC;IACb,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;IACd,uFAAuF;IACvF,OAAO,IAAI,IAAI,CAAC;IAChB,SAAS,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;CAC9E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-types.js","sourceRoot":"","sources":["../src/widget-types.ts"],"names":[],"mappings":""}
|
package/dist/widget.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FeedbackWidget, FeedbackWidgetOptions } from './widget-types.js';
|
|
2
|
+
export type { FeedbackWidget, FeedbackWidgetOptions } from './widget-types.js';
|
|
3
|
+
export type { FeedbackWidgetLabels } from './widget-labels.js';
|
|
4
|
+
/** Optional, framework-independent UI. Importing this module is safe during SSR. */
|
|
5
|
+
export declare function createFeedbackWidget(options: FeedbackWidgetOptions): FeedbackWidget;
|
|
6
|
+
//# sourceMappingURL=widget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE/E,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAa/D,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA4ZnF"}
|
package/dist/widget.js
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { FeedbackClient } from './client.js';
|
|
2
|
+
import { FeedbackCaptureError } from './errors.js';
|
|
3
|
+
import { resolveWidgetLabels } from './widget-labels.js';
|
|
4
|
+
import { widgetStyles } from './widget-styles.js';
|
|
5
|
+
const icons = {
|
|
6
|
+
record: '<rect x="3" y="6" width="12" height="12" rx="2"/><path d="m15 10 6-3v10l-6-3"/>',
|
|
7
|
+
screenshot: '<path d="m8 5 1-2h6l1 2h4v15H4V5z"/><circle cx="12" cy="12" r="4"/>',
|
|
8
|
+
text: '<path d="M14 2H5v20h14V7zM14 2v6h5M8 12h8M8 16h8"/>',
|
|
9
|
+
close: '<path d="m6 6 12 12M6 18 18 6"/>',
|
|
10
|
+
send: '<path d="m22 2-7 20-4-9-9-4 20-7ZM22 2 11 13"/>',
|
|
11
|
+
attach: '<path d="m20 11-8 8a5 5 0 0 1-7-7l9-9a3.5 3.5 0 0 1 5 5l-9 9a2 2 0 0 1-3-3l8-8"/>',
|
|
12
|
+
stop: '<rect x="5" y="5" width="14" height="14" rx="2"/>',
|
|
13
|
+
feedback: '<path d="M21 11a8 8 0 0 1-8 8H7l-5 3 2-6a8 8 0 0 1-1-5 9 9 0 0 1 18 0Z"/><path d="M8 10h8M8 14h5"/>',
|
|
14
|
+
};
|
|
15
|
+
/** Optional, framework-independent UI. Importing this module is safe during SSR. */
|
|
16
|
+
export function createFeedbackWidget(options) {
|
|
17
|
+
if (typeof document === 'undefined' || !document.body) {
|
|
18
|
+
throw new Error('Mount the feedback widget after the browser document is ready.');
|
|
19
|
+
}
|
|
20
|
+
const maxBytes = options.maxAttachmentBytes ?? 50 * 1024 * 1024;
|
|
21
|
+
if (!Number.isFinite(maxBytes) || maxBytes <= 0)
|
|
22
|
+
throw new Error('maxAttachmentBytes must be positive.');
|
|
23
|
+
const client = new FeedbackClient(options.transport);
|
|
24
|
+
const host = document.createElement('div');
|
|
25
|
+
host.dataset.artarchFeedback = '';
|
|
26
|
+
for (const [key, value] of Object.entries(options.theme ?? {}))
|
|
27
|
+
host.style.setProperty(key, value);
|
|
28
|
+
const root = host.attachShadow({ mode: 'open' });
|
|
29
|
+
const style = document.createElement('style');
|
|
30
|
+
if (options.nonce)
|
|
31
|
+
style.nonce = options.nonce;
|
|
32
|
+
style.textContent = widgetStyles + '\n' + (options.styles ?? '');
|
|
33
|
+
root.append(style);
|
|
34
|
+
const content = document.createElement('div');
|
|
35
|
+
// Only this static SDK-owned template uses HTML; all external text uses textContent.
|
|
36
|
+
content.innerHTML = `
|
|
37
|
+
<button class="entry" part="trigger" data-action="open"><svg data-icon="feedback"></svg><span data-label="trigger"></span></button>
|
|
38
|
+
<div class="frame" part="frame" hidden aria-hidden="true"></div>
|
|
39
|
+
<div class="toolbar" part="toolbar" role="toolbar" data-aria="toolbar" hidden>
|
|
40
|
+
<div class="modes">
|
|
41
|
+
<button data-action="record" aria-pressed="false"><svg data-icon="record"></svg><span data-label="record"></span></button>
|
|
42
|
+
<button data-action="screenshot" aria-pressed="false"><svg data-icon="screenshot"></svg><span data-label="screenshot"></span></button>
|
|
43
|
+
<button data-action="text" aria-pressed="false"><svg data-icon="text"></svg><span data-label="text"></span></button>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="recording-controls" hidden>
|
|
46
|
+
<span class="recording-dot" aria-hidden="true"></span><span class="timer" role="status"></span>
|
|
47
|
+
<button data-action="stop" class="primary"><svg data-icon="stop"></svg><span data-label="stop"></span></button>
|
|
48
|
+
</div>
|
|
49
|
+
<button class="icon-button" data-action="close" data-aria="close"><svg data-icon="close"></svg></button>
|
|
50
|
+
</div>
|
|
51
|
+
<dialog class="intro" part="intro" aria-labelledby="feedback-intro-title" aria-describedby="feedback-intro-body">
|
|
52
|
+
<h2 id="feedback-intro-title" data-label="introTitle"></h2>
|
|
53
|
+
<p id="feedback-intro-body" data-label="introBody"></p>
|
|
54
|
+
<div class="intro-actions">
|
|
55
|
+
<label><input type="checkbox"><span data-label="dontShowAgain"></span></label>
|
|
56
|
+
<button class="primary" data-action="acknowledge" autofocus data-label="acknowledge"></button>
|
|
57
|
+
</div>
|
|
58
|
+
</dialog>
|
|
59
|
+
<section class="text-panel" part="text-panel" data-aria="description" hidden></section>
|
|
60
|
+
<dialog class="review" part="review" aria-labelledby="feedback-review-title">
|
|
61
|
+
<header class="review-header"><h2 id="feedback-review-title" data-label="description"></h2>
|
|
62
|
+
<button class="icon-button" data-action="close" data-aria="close"><svg data-icon="close"></svg></button>
|
|
63
|
+
</header>
|
|
64
|
+
<div class="preview" part="preview">
|
|
65
|
+
<img data-aria="screenshotPreview" hidden>
|
|
66
|
+
<video controls playsinline data-aria="recordingPreview" hidden></video>
|
|
67
|
+
<button class="icon-button remove" data-action="remove" data-aria="removeAttachment"><svg data-icon="close"></svg></button>
|
|
68
|
+
<span class="filename"></span>
|
|
69
|
+
</div>
|
|
70
|
+
</dialog>
|
|
71
|
+
<div class="notice" part="notice" role="status" hidden><span></span><button class="icon-button" data-action="dismiss" data-aria="close"><svg data-icon="close"></svg></button></div>
|
|
72
|
+
`;
|
|
73
|
+
root.append(content);
|
|
74
|
+
const form = document.createElement('form');
|
|
75
|
+
form.setAttribute('part', 'form');
|
|
76
|
+
form.innerHTML = `
|
|
77
|
+
<label class="sr-only" for="feedback-message" data-label="description"></label>
|
|
78
|
+
<textarea id="feedback-message" name="message" part="textarea" maxlength="5000" required></textarea>
|
|
79
|
+
<p class="error" role="alert" hidden></p>
|
|
80
|
+
<div class="footer" part="footer">
|
|
81
|
+
<button type="button" class="icon-button attach" data-action="attach" data-aria="attach"><svg data-icon="attach"></svg></button>
|
|
82
|
+
<input class="file-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif,video/mp4,video/webm" hidden>
|
|
83
|
+
<button type="button" data-action="close" data-label="cancel"></button>
|
|
84
|
+
<button type="submit" class="primary send"><svg data-icon="send"></svg><span data-label="send"></span></button>
|
|
85
|
+
</div>
|
|
86
|
+
`;
|
|
87
|
+
const query = (selector) => root.querySelector(selector);
|
|
88
|
+
const entry = query('.entry');
|
|
89
|
+
const toolbar = query('.toolbar');
|
|
90
|
+
const frame = query('.frame');
|
|
91
|
+
const intro = query('.intro');
|
|
92
|
+
const review = query('.review');
|
|
93
|
+
const panel = query('.text-panel');
|
|
94
|
+
panel.append(form);
|
|
95
|
+
const textarea = query('textarea');
|
|
96
|
+
const fileInput = query('.file-input');
|
|
97
|
+
const submitButton = query('.send');
|
|
98
|
+
const errorMessage = query('.error');
|
|
99
|
+
const notice = query('.notice');
|
|
100
|
+
const previewImage = query('.preview img');
|
|
101
|
+
const previewVideo = query('.preview video');
|
|
102
|
+
const timer = query('.timer');
|
|
103
|
+
const modes = query('.modes');
|
|
104
|
+
const recordingControls = query('.recording-controls');
|
|
105
|
+
const stopButton = query('[data-action="stop"]');
|
|
106
|
+
for (const svg of root.querySelectorAll('svg')) {
|
|
107
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
108
|
+
svg.setAttribute('fill', 'none');
|
|
109
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
110
|
+
svg.setAttribute('stroke-width', '1.6');
|
|
111
|
+
svg.setAttribute('stroke-linecap', 'round');
|
|
112
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
113
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
114
|
+
svg.innerHTML = icons[svg.dataset.icon] ?? '';
|
|
115
|
+
}
|
|
116
|
+
for (const button of root.querySelectorAll('button:not(.send)'))
|
|
117
|
+
button.type = 'button';
|
|
118
|
+
let labels = resolveWidgetLabels(options.locale ?? 'en', options.labels);
|
|
119
|
+
let active = false;
|
|
120
|
+
let destroyed = false;
|
|
121
|
+
let capture;
|
|
122
|
+
let submission;
|
|
123
|
+
let attachment;
|
|
124
|
+
let previewUrl;
|
|
125
|
+
let interval;
|
|
126
|
+
let noticeTimeout;
|
|
127
|
+
let previousFocus;
|
|
128
|
+
let introSeen = false;
|
|
129
|
+
const storageKey = options.introduction === false ? ''
|
|
130
|
+
: options.introduction?.storageKey ?? 'artarch-feedback-sdk:hide-introduction';
|
|
131
|
+
function setLocale(locale, overrides = options.labels) {
|
|
132
|
+
labels = resolveWidgetLabels(locale, overrides);
|
|
133
|
+
host.lang = locale;
|
|
134
|
+
for (const node of root.querySelectorAll('[data-label]')) {
|
|
135
|
+
node.textContent = labels[node.dataset.label];
|
|
136
|
+
}
|
|
137
|
+
for (const node of root.querySelectorAll('[data-aria]')) {
|
|
138
|
+
node.setAttribute('aria-label', labels[node.dataset.aria]);
|
|
139
|
+
}
|
|
140
|
+
previewImage.alt = labels.screenshotPreview;
|
|
141
|
+
textarea.placeholder = labels.placeholder;
|
|
142
|
+
updateSubmit();
|
|
143
|
+
}
|
|
144
|
+
function updateSubmit() {
|
|
145
|
+
submitButton.disabled = Boolean(submission) || !textarea.value.trim();
|
|
146
|
+
submitButton.querySelector('span').textContent = submission ? labels.sending : labels.send;
|
|
147
|
+
textarea.disabled = Boolean(submission);
|
|
148
|
+
query('[data-action="attach"]').disabled = Boolean(submission);
|
|
149
|
+
query('[data-action="remove"]').disabled = Boolean(submission);
|
|
150
|
+
form.setAttribute('aria-busy', String(Boolean(submission)));
|
|
151
|
+
}
|
|
152
|
+
function showNotice(message, isError = false) {
|
|
153
|
+
clearTimeout(noticeTimeout);
|
|
154
|
+
notice.querySelector('span').textContent = message;
|
|
155
|
+
notice.dataset.error = String(isError);
|
|
156
|
+
notice.hidden = false;
|
|
157
|
+
if (!isError)
|
|
158
|
+
noticeTimeout = setTimeout(() => { notice.hidden = true; }, 5000);
|
|
159
|
+
}
|
|
160
|
+
function clearAttachment() {
|
|
161
|
+
previewVideo.pause();
|
|
162
|
+
previewVideo.removeAttribute('src');
|
|
163
|
+
previewVideo.load();
|
|
164
|
+
previewImage.removeAttribute('src');
|
|
165
|
+
previewImage.hidden = previewVideo.hidden = true;
|
|
166
|
+
if (previewUrl)
|
|
167
|
+
URL.revokeObjectURL(previewUrl);
|
|
168
|
+
previewUrl = undefined;
|
|
169
|
+
attachment = undefined;
|
|
170
|
+
fileInput.value = '';
|
|
171
|
+
}
|
|
172
|
+
function chooseMode(mode) {
|
|
173
|
+
for (const button of modes.querySelectorAll('button')) {
|
|
174
|
+
button.setAttribute('aria-pressed', String(button.dataset.action === mode));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function showText() {
|
|
178
|
+
if (destroyed || capture || submission)
|
|
179
|
+
return;
|
|
180
|
+
notice.hidden = true;
|
|
181
|
+
review.close();
|
|
182
|
+
clearAttachment();
|
|
183
|
+
panel.append(form);
|
|
184
|
+
panel.hidden = false;
|
|
185
|
+
chooseMode('text');
|
|
186
|
+
textarea.focus();
|
|
187
|
+
}
|
|
188
|
+
function showAttachment(asset) {
|
|
189
|
+
if (asset.blob.size > maxBytes) {
|
|
190
|
+
if (!review.open) {
|
|
191
|
+
panel.hidden = false;
|
|
192
|
+
panel.append(form);
|
|
193
|
+
}
|
|
194
|
+
errorMessage.textContent = labels.attachmentTooLarge;
|
|
195
|
+
errorMessage.hidden = false;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
clearAttachment();
|
|
199
|
+
attachment = asset;
|
|
200
|
+
previewUrl = URL.createObjectURL(asset.blob);
|
|
201
|
+
const media = asset.kind === 'screenshot' ? previewImage : previewVideo;
|
|
202
|
+
media.src = previewUrl;
|
|
203
|
+
media.hidden = false;
|
|
204
|
+
query('.filename').textContent = asset.filename;
|
|
205
|
+
panel.hidden = true;
|
|
206
|
+
review.append(form);
|
|
207
|
+
if (!review.open)
|
|
208
|
+
review.showModal();
|
|
209
|
+
textarea.focus();
|
|
210
|
+
}
|
|
211
|
+
function resetCaptureControls() {
|
|
212
|
+
clearInterval(interval);
|
|
213
|
+
interval = undefined;
|
|
214
|
+
modes.hidden = false;
|
|
215
|
+
recordingControls.hidden = true;
|
|
216
|
+
host.style.visibility = '';
|
|
217
|
+
}
|
|
218
|
+
function close() {
|
|
219
|
+
if (destroyed)
|
|
220
|
+
return;
|
|
221
|
+
const wasActive = active;
|
|
222
|
+
active = false;
|
|
223
|
+
capture?.abort();
|
|
224
|
+
submission?.abort();
|
|
225
|
+
capture = submission = undefined;
|
|
226
|
+
resetCaptureControls();
|
|
227
|
+
intro.close();
|
|
228
|
+
review.close();
|
|
229
|
+
panel.hidden = toolbar.hidden = frame.hidden = notice.hidden = true;
|
|
230
|
+
clearTimeout(noticeTimeout);
|
|
231
|
+
clearAttachment();
|
|
232
|
+
panel.append(form);
|
|
233
|
+
textarea.value = '';
|
|
234
|
+
errorMessage.hidden = true;
|
|
235
|
+
entry.hidden = options.trigger === false;
|
|
236
|
+
updateSubmit();
|
|
237
|
+
if (wasActive) {
|
|
238
|
+
(previousFocus?.isConnected ? previousFocus : entry).focus();
|
|
239
|
+
options.onClose?.();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function open() {
|
|
243
|
+
if (destroyed || active)
|
|
244
|
+
return;
|
|
245
|
+
const focused = root.activeElement ?? document.activeElement;
|
|
246
|
+
previousFocus = focused instanceof HTMLElement ? focused : undefined;
|
|
247
|
+
active = true;
|
|
248
|
+
entry.hidden = notice.hidden = true;
|
|
249
|
+
toolbar.hidden = frame.hidden = false;
|
|
250
|
+
let skip = options.introduction === false || introSeen;
|
|
251
|
+
try {
|
|
252
|
+
skip || (skip = localStorage.getItem(storageKey) === 'true');
|
|
253
|
+
}
|
|
254
|
+
catch { /* Storage is optional. */ }
|
|
255
|
+
if (!skip)
|
|
256
|
+
intro.showModal();
|
|
257
|
+
else
|
|
258
|
+
query('[data-action="screenshot"]').focus();
|
|
259
|
+
}
|
|
260
|
+
function captureError(error) {
|
|
261
|
+
const code = error instanceof FeedbackCaptureError ? error.code : '';
|
|
262
|
+
if (code === 'cancelled')
|
|
263
|
+
return;
|
|
264
|
+
showNotice(code === 'unsupported' ? labels.unsupported
|
|
265
|
+
: code === 'permission-denied' ? labels.permissionDenied : labels.captureFailed, true);
|
|
266
|
+
}
|
|
267
|
+
async function takeScreenshot() {
|
|
268
|
+
if (capture || submission)
|
|
269
|
+
return;
|
|
270
|
+
const controller = new AbortController();
|
|
271
|
+
capture = controller;
|
|
272
|
+
notice.hidden = panel.hidden = true;
|
|
273
|
+
errorMessage.hidden = true;
|
|
274
|
+
chooseMode('screenshot');
|
|
275
|
+
// Hide before the native call, without awaiting and losing the user gesture.
|
|
276
|
+
host.style.visibility = 'hidden';
|
|
277
|
+
try {
|
|
278
|
+
const asset = await client.captureScreenshot({ ...options.screenshot, signal: controller.signal });
|
|
279
|
+
if (destroyed || capture !== controller)
|
|
280
|
+
return;
|
|
281
|
+
host.style.visibility = '';
|
|
282
|
+
capture = undefined;
|
|
283
|
+
showAttachment(asset);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (!destroyed && capture === controller)
|
|
287
|
+
captureError(error);
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
if (capture === controller)
|
|
291
|
+
capture = undefined;
|
|
292
|
+
if (!destroyed && !capture)
|
|
293
|
+
host.style.visibility = '';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function record() {
|
|
297
|
+
if (capture || submission)
|
|
298
|
+
return;
|
|
299
|
+
const controller = new AbortController();
|
|
300
|
+
capture = controller;
|
|
301
|
+
notice.hidden = panel.hidden = true;
|
|
302
|
+
errorMessage.hidden = true;
|
|
303
|
+
chooseMode('record');
|
|
304
|
+
modes.hidden = true;
|
|
305
|
+
recordingControls.hidden = false;
|
|
306
|
+
stopButton.disabled = true;
|
|
307
|
+
timer.textContent = labels.requesting;
|
|
308
|
+
try {
|
|
309
|
+
await client.startRecording({ maxDurationMs: 60000, ...options.recording, signal: controller.signal });
|
|
310
|
+
if (destroyed || capture !== controller)
|
|
311
|
+
return;
|
|
312
|
+
stopButton.disabled = false;
|
|
313
|
+
const started = Date.now();
|
|
314
|
+
const tick = () => {
|
|
315
|
+
const seconds = Math.floor((Date.now() - started) / 1000);
|
|
316
|
+
timer.textContent = `${labels.recording} ${Math.floor(seconds / 60).toString().padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
|
|
317
|
+
};
|
|
318
|
+
tick();
|
|
319
|
+
interval = setInterval(tick, 500);
|
|
320
|
+
const asset = await client.recorder.finished;
|
|
321
|
+
if (destroyed || capture !== controller)
|
|
322
|
+
return;
|
|
323
|
+
capture = undefined;
|
|
324
|
+
resetCaptureControls();
|
|
325
|
+
showAttachment(asset);
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
if (!destroyed && capture === controller)
|
|
329
|
+
captureError(error);
|
|
330
|
+
}
|
|
331
|
+
finally {
|
|
332
|
+
if (capture === controller) {
|
|
333
|
+
capture = undefined;
|
|
334
|
+
resetCaptureControls();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async function submit(event) {
|
|
339
|
+
event.preventDefault();
|
|
340
|
+
if (capture || submission || !textarea.value.trim())
|
|
341
|
+
return;
|
|
342
|
+
const controller = new AbortController();
|
|
343
|
+
submission = controller;
|
|
344
|
+
errorMessage.hidden = true;
|
|
345
|
+
updateSubmit();
|
|
346
|
+
let result;
|
|
347
|
+
try {
|
|
348
|
+
result = await client.submit({
|
|
349
|
+
message: textarea.value.trim(),
|
|
350
|
+
...(attachment?.kind === 'screenshot' ? { screenshot: attachment } : { recording: attachment }),
|
|
351
|
+
context: typeof options.context === 'function' ? options.context() : options.context,
|
|
352
|
+
signal: controller.signal,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
if (!destroyed && submission === controller) {
|
|
357
|
+
let message = labels.submitFailed;
|
|
358
|
+
try {
|
|
359
|
+
message = options.formatSubmitError?.(error) || message;
|
|
360
|
+
}
|
|
361
|
+
catch { /* Keep the draft usable if host formatting fails. */ }
|
|
362
|
+
errorMessage.textContent = message;
|
|
363
|
+
errorMessage.hidden = false;
|
|
364
|
+
}
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
if (submission === controller) {
|
|
369
|
+
submission = undefined;
|
|
370
|
+
updateSubmit();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (destroyed || controller.signal.aborted)
|
|
374
|
+
return;
|
|
375
|
+
close();
|
|
376
|
+
showNotice(labels.success);
|
|
377
|
+
options.onSuccess?.(result);
|
|
378
|
+
}
|
|
379
|
+
function onClick(event) {
|
|
380
|
+
const target = event.target instanceof Element ? event.target.closest('button[data-action]') : null;
|
|
381
|
+
if (!target || target.disabled)
|
|
382
|
+
return;
|
|
383
|
+
switch (target.dataset.action) {
|
|
384
|
+
case 'open':
|
|
385
|
+
open();
|
|
386
|
+
break;
|
|
387
|
+
case 'close':
|
|
388
|
+
close();
|
|
389
|
+
break;
|
|
390
|
+
case 'dismiss':
|
|
391
|
+
notice.hidden = true;
|
|
392
|
+
break;
|
|
393
|
+
case 'acknowledge':
|
|
394
|
+
introSeen = true;
|
|
395
|
+
if (query('.intro input').checked) {
|
|
396
|
+
try {
|
|
397
|
+
localStorage.setItem(storageKey, 'true');
|
|
398
|
+
}
|
|
399
|
+
catch { /* Keep working without storage. */ }
|
|
400
|
+
}
|
|
401
|
+
intro.close();
|
|
402
|
+
query('[data-action="screenshot"]').focus();
|
|
403
|
+
break;
|
|
404
|
+
case 'text':
|
|
405
|
+
case 'remove':
|
|
406
|
+
showText();
|
|
407
|
+
break;
|
|
408
|
+
case 'screenshot':
|
|
409
|
+
void takeScreenshot();
|
|
410
|
+
break;
|
|
411
|
+
case 'record':
|
|
412
|
+
void record();
|
|
413
|
+
break;
|
|
414
|
+
case 'stop':
|
|
415
|
+
stopButton.disabled = true;
|
|
416
|
+
clearInterval(interval);
|
|
417
|
+
timer.textContent = labels.finalizing;
|
|
418
|
+
// The finished promise in record() owns both manual and automatic completion.
|
|
419
|
+
void client.stopRecording().catch(() => { });
|
|
420
|
+
break;
|
|
421
|
+
case 'attach':
|
|
422
|
+
if (!submission)
|
|
423
|
+
fileInput.click();
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function attachFile() {
|
|
428
|
+
if (submission || capture)
|
|
429
|
+
return;
|
|
430
|
+
const file = fileInput.files?.[0];
|
|
431
|
+
if (!file)
|
|
432
|
+
return;
|
|
433
|
+
fileInput.value = '';
|
|
434
|
+
errorMessage.hidden = true;
|
|
435
|
+
if (!/^(image\/(png|jpeg|webp|gif)|video\/(mp4|webm))$/.test(file.type)) {
|
|
436
|
+
errorMessage.textContent = labels.invalidAttachment;
|
|
437
|
+
errorMessage.hidden = false;
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
showAttachment({ kind: file.type.startsWith('image/') ? 'screenshot' : 'recording',
|
|
441
|
+
blob: file, contentType: file.type, filename: file.name });
|
|
442
|
+
}
|
|
443
|
+
const onCancel = (event) => { event.preventDefault(); close(); };
|
|
444
|
+
const onKey = (event) => {
|
|
445
|
+
if (event.key === 'Escape' && active && !intro.open && !review.open) {
|
|
446
|
+
event.preventDefault();
|
|
447
|
+
close();
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
root.addEventListener('click', onClick);
|
|
451
|
+
root.addEventListener('keydown', event => {
|
|
452
|
+
// Keep text entry and shortcuts out of the host editor's bubble listeners.
|
|
453
|
+
event.stopPropagation();
|
|
454
|
+
onKey(event);
|
|
455
|
+
});
|
|
456
|
+
intro.addEventListener('cancel', onCancel);
|
|
457
|
+
review.addEventListener('cancel', onCancel);
|
|
458
|
+
document.addEventListener('keydown', onKey);
|
|
459
|
+
textarea.addEventListener('input', updateSubmit);
|
|
460
|
+
fileInput.addEventListener('change', attachFile);
|
|
461
|
+
form.addEventListener('submit', submit);
|
|
462
|
+
entry.hidden = options.trigger === false;
|
|
463
|
+
setLocale(options.locale ?? 'en');
|
|
464
|
+
(options.container ?? document.body).append(host);
|
|
465
|
+
return {
|
|
466
|
+
element: host, open, close, setLocale,
|
|
467
|
+
destroy() {
|
|
468
|
+
if (destroyed)
|
|
469
|
+
return;
|
|
470
|
+
try {
|
|
471
|
+
close();
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
destroyed = true;
|
|
475
|
+
document.removeEventListener('keydown', onKey);
|
|
476
|
+
host.remove();
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
//# sourceMappingURL=widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.js","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAE,mBAAmB,EAA6B,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMlD,MAAM,KAAK,GAA2B;IACpC,MAAM,EAAE,iFAAiF;IACzF,UAAU,EAAE,qEAAqE;IACjF,IAAI,EAAE,qDAAqD;IAC3D,KAAK,EAAE,kCAAkC;IACzC,IAAI,EAAE,iDAAiD;IACvD,MAAM,EAAE,mFAAmF;IAC3F,IAAI,EAAE,mDAAmD;IACzD,QAAQ,EAAE,qGAAqG;CAChH,CAAC;AAEF,oFAAoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,OAA8B;IACjE,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACzG,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACnG,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,OAAO,CAAC,KAAK;QAAE,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC/C,KAAK,CAAC,WAAW,GAAG,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnB,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,qFAAqF;IACrF,OAAO,CAAC,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCnB,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,SAAS,GAAG;;;;;;;;;;GAUhB,CAAC;IACF,MAAM,KAAK,GAAG,CAAoB,QAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAI,QAAQ,CAAE,CAAC;IACxF,MAAM,KAAK,GAAG,KAAK,CAAoB,QAAQ,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,KAAK,CAAiB,UAAU,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,KAAK,CAAiB,QAAQ,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,KAAK,CAAoB,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,KAAK,CAAoB,SAAS,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,KAAK,CAAc,aAAa,CAAC,CAAC;IAChD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnB,MAAM,QAAQ,GAAG,KAAK,CAAsB,UAAU,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,KAAK,CAAmB,aAAa,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,KAAK,CAAoB,OAAO,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,KAAK,CAAuB,QAAQ,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAiB,SAAS,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,KAAK,CAAmB,cAAc,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,KAAK,CAAmB,gBAAgB,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,KAAK,CAAkB,QAAQ,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,KAAK,CAAiB,QAAQ,CAAC,CAAC;IAC9C,MAAM,iBAAiB,GAAG,KAAK,CAAiB,qBAAqB,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,KAAK,CAAoB,sBAAsB,CAAC,CAAC;IACpE,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,gBAAgB,CAAa,KAAK,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACzC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QAC3C,GAAG,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QACxC,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC5C,GAAG,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QAC7C,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACxC,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAK,CAAC,IAAI,EAAE,CAAC;IACjD,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAoB,mBAAmB,CAAC;QAAE,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;IAE3G,IAAI,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,OAAoC,CAAC;IACzC,IAAI,UAAuC,CAAC;IAC5C,IAAI,UAAqC,CAAC;IAC1C,IAAI,UAA8B,CAAC;IACnC,IAAI,QAAoD,CAAC;IACzD,IAAI,aAAwD,CAAC;IAC7D,IAAI,aAAsC,CAAC;IAC3C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE;QACpD,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,UAAU,IAAI,wCAAwC,CAAC;IAEjF,SAAS,SAAS,CAAC,MAAmB,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM;QAChE,MAAM,GAAG,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,gBAAgB,CAAc,cAAc,CAAC,EAAE,CAAC;YACtE,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAmC,CAAC,CAAC;QAC9E,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,gBAAgB,CAAc,aAAa,CAAC,EAAE,CAAC;YACrE,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAkC,CAAC,CAAC,CAAC;QAC3F,CAAC;QACD,YAAY,CAAC,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC;QAC5C,QAAQ,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QAC1C,YAAY,EAAE,CAAC;IACjB,CAAC;IAED,SAAS,YAAY;QACnB,YAAY,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACtE,YAAY,CAAC,aAAa,CAAC,MAAM,CAAE,CAAC,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5F,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,KAAK,CAAoB,wBAAwB,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QAClF,KAAK,CAAoB,wBAAwB,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QAClF,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,SAAS,UAAU,CAAC,OAAe,EAAE,OAAO,GAAG,KAAK;QAClD,YAAY,CAAC,aAAa,CAAC,CAAC;QAC5B,MAAM,CAAC,aAAa,CAAC,MAAM,CAAE,CAAC,WAAW,GAAG,OAAO,CAAC;QACpD,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,OAAO;YAAE,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAClF,CAAC;IAED,SAAS,eAAe;QACtB,YAAY,CAAC,KAAK,EAAE,CAAC;QACrB,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACpC,YAAY,CAAC,IAAI,EAAE,CAAC;QACpB,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACpC,YAAY,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC;QACjD,IAAI,UAAU;YAAE,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QAChD,UAAU,GAAG,SAAS,CAAC;QACvB,UAAU,GAAG,SAAS,CAAC;QACvB,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC;IACvB,CAAC;IAED,SAAS,UAAU,CAAC,IAAsC;QACxD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAoB,QAAQ,CAAC,EAAE,CAAC;YACzE,MAAM,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,SAAS,QAAQ;QACf,IAAI,SAAS,IAAI,OAAO,IAAI,UAAU;YAAE,OAAO;QAC/C,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,eAAe,EAAE,CAAC;QAClB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACrB,UAAU,CAAC,MAAM,CAAC,CAAC;QACnB,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,SAAS,cAAc,CAAC,KAAoB;QAC1C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;gBAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAAC,CAAC;YAC/D,YAAY,CAAC,WAAW,GAAG,MAAM,CAAC,kBAAkB,CAAC;YACrD,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,eAAe,EAAE,CAAC;QAClB,UAAU,GAAG,KAAK,CAAC;QACnB,UAAU,GAAG,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;QACxE,KAAK,CAAC,GAAG,GAAG,UAAU,CAAC;QACvB,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACrB,KAAK,CAAc,WAAW,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC7D,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,SAAS,oBAAoB;QAC3B,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxB,QAAQ,GAAG,SAAS,CAAC;QACrB,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACrB,iBAAiB,CAAC,MAAM,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;IAC7B,CAAC;IAED,SAAS,KAAK;QACZ,IAAI,SAAS;YAAE,OAAO;QACtB,MAAM,SAAS,GAAG,MAAM,CAAC;QACzB,MAAM,GAAG,KAAK,CAAC;QACf,OAAO,EAAE,KAAK,EAAE,CAAC;QACjB,UAAU,EAAE,KAAK,EAAE,CAAC;QACpB,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;QACjC,oBAAoB,EAAE,CAAC;QACvB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACpE,YAAY,CAAC,aAAa,CAAC,CAAC;QAC5B,eAAe,EAAE,CAAC;QAClB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;QACpB,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC;QACzC,YAAY,EAAE,CAAC;QACf,IAAI,SAAS,EAAE,CAAC;YACd,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;YAC7D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED,SAAS,IAAI;QACX,IAAI,SAAS,IAAI,MAAM;YAAE,OAAO;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC;QAC7D,aAAa,GAAG,OAAO,YAAY,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,MAAM,GAAG,IAAI,CAAC;QACd,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACpC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACtC,IAAI,IAAI,GAAG,OAAO,CAAC,YAAY,KAAK,KAAK,IAAI,SAAS,CAAC;QACvD,IAAI,CAAC;YAAC,IAAI,KAAJ,IAAI,GAAK,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,MAAM,EAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,0BAA0B,CAAC,CAAC;QAClG,IAAI,CAAC,IAAI;YAAE,KAAK,CAAC,SAAS,EAAE,CAAC;;YACxB,KAAK,CAAoB,4BAA4B,CAAC,CAAC,KAAK,EAAE,CAAC;IACtE,CAAC;IAED,SAAS,YAAY,CAAC,KAAc;QAClC,MAAM,IAAI,GAAG,KAAK,YAAY,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,IAAI,IAAI,KAAK,WAAW;YAAE,OAAO;QACjC,UAAU,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW;YACpD,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAC3F,CAAC;IAED,KAAK,UAAU,cAAc;QAC3B,IAAI,OAAO,IAAI,UAAU;YAAE,OAAO;QAClC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,OAAO,GAAG,UAAU,CAAC;QACrB,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,YAAY,CAAC,CAAC;QACzB,6EAA6E;QAC7E,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YACnG,IAAI,SAAS,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO;YAChD,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;YAC3B,OAAO,GAAG,SAAS,CAAC;YACpB,cAAc,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,IAAI,OAAO,KAAK,UAAU;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAChE,CAAC;gBAAS,CAAC;YACT,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO,GAAG,SAAS,CAAC;YAChD,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO;gBAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IAED,KAAK,UAAU,MAAM;QACnB,IAAI,OAAO,IAAI,UAAU;YAAE,OAAO;QAClC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,OAAO,GAAG,UAAU,CAAC;QACrB,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,QAAQ,CAAC,CAAC;QACrB,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,iBAAiB,CAAC,MAAM,GAAG,KAAK,CAAC;QACjC,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC3B,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,cAAc,CAAC,EAAE,aAAa,EAAE,KAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YACxG,IAAI,SAAS,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO;YAChD,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC;YAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC1D,KAAK,CAAC,WAAW,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;YAClJ,CAAC,CAAC;YACF,IAAI,EAAE,CAAC;YACP,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC7C,IAAI,SAAS,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO;YAChD,OAAO,GAAG,SAAS,CAAC;YACpB,oBAAoB,EAAE,CAAC;YACvB,cAAc,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,IAAI,OAAO,KAAK,UAAU;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAChE,CAAC;gBAAS,CAAC;YACT,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;gBAC3B,OAAO,GAAG,SAAS,CAAC;gBACpB,oBAAoB,EAAE,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,UAAU,MAAM,CAAC,KAAkB;QACtC,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,OAAO,IAAI,UAAU,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,OAAO;QAC5D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,UAAU,GAAG,UAAU,CAAC;QACxB,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,YAAY,EAAE,CAAC;QACf,IAAI,MAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;gBAC3B,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;gBAC9B,GAAG,CAAC,UAAU,EAAE,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;gBAC/F,OAAO,EAAE,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO;gBACpF,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;gBAC5C,IAAI,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC;gBAClC,IAAI,CAAC;oBAAC,OAAO,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,qDAAqD,CAAC,CAAC;gBAChI,YAAY,CAAC,WAAW,GAAG,OAAO,CAAC;gBACnC,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;YAC9B,CAAC;YACD,OAAO;QACT,CAAC;gBAAS,CAAC;YACT,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;gBAAC,UAAU,GAAG,SAAS,CAAC;gBAAC,YAAY,EAAE,CAAC;YAAC,CAAC;QAC5E,CAAC;QACD,IAAI,SAAS,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO;QACnD,KAAK,EAAE,CAAC;QACR,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,SAAS,OAAO,CAAC,KAAY;QAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAoB,qBAAqB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACvH,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO;QACvC,QAAQ,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC9B,KAAK,MAAM;gBAAE,IAAI,EAAE,CAAC;gBAAC,MAAM;YAC3B,KAAK,OAAO;gBAAE,KAAK,EAAE,CAAC;gBAAC,MAAM;YAC7B,KAAK,SAAS;gBAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;gBAAC,MAAM;YAC5C,KAAK,aAAa;gBAChB,SAAS,GAAG,IAAI,CAAC;gBACjB,IAAI,KAAK,CAAmB,cAAc,CAAC,CAAC,OAAO,EAAE,CAAC;oBACpD,IAAI,CAAC;wBAAC,YAAY,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,mCAAmC,CAAC,CAAC;gBACjG,CAAC;gBACD,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,KAAK,CAAoB,4BAA4B,CAAC,CAAC,KAAK,EAAE,CAAC;gBAC/D,MAAM;YACR,KAAK,MAAM,CAAC;YAAC,KAAK,QAAQ;gBAAE,QAAQ,EAAE,CAAC;gBAAC,MAAM;YAC9C,KAAK,YAAY;gBAAE,KAAK,cAAc,EAAE,CAAC;gBAAC,MAAM;YAChD,KAAK,QAAQ;gBAAE,KAAK,MAAM,EAAE,CAAC;gBAAC,MAAM;YACpC,KAAK,MAAM;gBACT,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC3B,aAAa,CAAC,QAAQ,CAAC,CAAC;gBACxB,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;gBACtC,8EAA8E;gBAC9E,KAAK,MAAM,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC5C,MAAM;YACR,KAAK,QAAQ;gBAAE,IAAI,CAAC,UAAU;oBAAE,SAAS,CAAC,KAAK,EAAE,CAAC;gBAAC,MAAM;QAC3D,CAAC;IACH,CAAC;IAED,SAAS,UAAU;QACjB,IAAI,UAAU,IAAI,OAAO;YAAE,OAAO;QAClC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC;QACrB,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,kDAAkD,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,YAAY,CAAC,WAAW,GAAG,MAAM,CAAC,iBAAiB,CAAC;YACpD,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW;YAChF,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAE,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,CAAC,KAAoB,EAAE,EAAE;QACrC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAAC,KAAK,EAAE,CAAC;QAAC,CAAC;IAC3G,CAAC,CAAC;IACF,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE;QACvC,2EAA2E;QAC3E,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,KAAK,CAAC,KAAsB,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC3C,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5C,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjD,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACjD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC;IACzC,SAAS,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;IAClC,CAAC,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAElD,OAAO;QACL,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS;QACrC,OAAO;YACL,IAAI,SAAS;gBAAE,OAAO;YACtB,IAAI,CAAC;gBAAC,KAAK,EAAE,CAAC;YAAC,CAAC;oBAAS,CAAC;gBACxB,SAAS,GAAG,IAAI,CAAC;gBACjB,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "artarch-feedback-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Browser feedback SDK with screenshots, screen recordings, a headless core, and an optional customizable widget.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -18,10 +18,16 @@
|
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"import": "./dist/index.js",
|
|
20
20
|
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./widget": {
|
|
23
|
+
"types": "./dist/widget.d.ts",
|
|
24
|
+
"import": "./dist/widget.js",
|
|
25
|
+
"default": "./dist/widget.js"
|
|
21
26
|
}
|
|
22
27
|
},
|
|
23
28
|
"scripts": {
|
|
24
29
|
"build": "tsc -p tsconfig.build.json",
|
|
30
|
+
"prepare": "npm run build",
|
|
25
31
|
"test": "npm run build && node --test --test-timeout=15000 tests/*.test.mjs",
|
|
26
32
|
"test:browser": "npm run build && playwright test",
|
|
27
33
|
"test:all": "npm test && npm run test:browser",
|