@mdbase-dev/ui 0.1.0-beta.126 → 0.1.0-beta.148

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.
Files changed (74) hide show
  1. package/README.md +168 -0
  2. package/css/collections.css +978 -0
  3. package/css/controls.css +33 -1
  4. package/css/screens.css +44 -2
  5. package/dist/AppSwitcher.js +187 -33
  6. package/dist/Checkbox.d.ts +14 -0
  7. package/dist/Checkbox.js +32 -0
  8. package/dist/CollectionModeControl.d.ts +11 -0
  9. package/dist/CollectionModeControl.js +290 -0
  10. package/dist/CommandPalette.d.ts +4 -2
  11. package/dist/CommandPalette.js +275 -103
  12. package/dist/Dialog.js +72 -25
  13. package/dist/Feedback.js +763 -229
  14. package/dist/FeedbackMarkup.d.ts +1 -1
  15. package/dist/FeedbackMarkup.js +326 -125
  16. package/dist/FeedbackVerification.js +115 -68
  17. package/dist/SaveNotice.js +71 -28
  18. package/dist/Select.js +183 -135
  19. package/dist/SelectList.js +59 -14
  20. package/dist/ThemeSelect.js +55 -4
  21. package/dist/apps.d.ts +1 -0
  22. package/dist/apps.js +50 -29
  23. package/dist/brand.js +251 -68
  24. package/dist/codemirror.js +116 -87
  25. package/dist/collections/CollectionGate.d.ts +52 -0
  26. package/dist/collections/CollectionGate.js +456 -0
  27. package/dist/collections/CollectionSaveWarning.d.ts +10 -0
  28. package/dist/collections/CollectionSaveWarning.js +28 -0
  29. package/dist/collections/CollectionSwitcher.d.ts +51 -0
  30. package/dist/collections/CollectionSwitcher.js +598 -0
  31. package/dist/collections/SetupReview.d.ts +9 -0
  32. package/dist/collections/SetupReview.js +216 -0
  33. package/dist/collections/copy.d.ts +100 -0
  34. package/dist/collections/copy.js +937 -0
  35. package/dist/collections/dialogs.d.ts +27 -0
  36. package/dist/collections/dialogs.js +169 -0
  37. package/dist/collections/model.d.ts +146 -0
  38. package/dist/collections/model.js +337 -0
  39. package/dist/collections/mount.d.ts +19 -0
  40. package/dist/collections/mount.js +56 -0
  41. package/dist/collections/parts.d.ts +86 -0
  42. package/dist/collections/parts.js +439 -0
  43. package/dist/collections/testing.d.ts +19 -0
  44. package/dist/collections/testing.js +39 -0
  45. package/dist/collections/use-collection-ui.d.ts +140 -0
  46. package/dist/collections/use-collection-ui.js +236 -0
  47. package/dist/collections.d.ts +19 -0
  48. package/dist/collections.js +13 -0
  49. package/dist/feedback-data.d.ts +13 -12
  50. package/dist/feedback-data.js +365 -168
  51. package/dist/flock.d.ts +63 -0
  52. package/dist/flock.js +140 -0
  53. package/dist/i18n.d.ts +6 -0
  54. package/dist/i18n.js +71 -0
  55. package/dist/locales/de/messages.json +1 -0
  56. package/dist/locales/en/messages.json +1 -0
  57. package/dist/locales/es/messages.json +1 -0
  58. package/dist/locales/fr/messages.json +1 -0
  59. package/dist/locales/it/messages.json +1 -0
  60. package/dist/locales/ja/messages.json +1 -0
  61. package/dist/locales/ko/messages.json +1 -0
  62. package/dist/locales/pt/messages.json +1 -0
  63. package/dist/locales/ru/messages.json +1 -0
  64. package/dist/locales/zh/messages.json +1 -0
  65. package/dist/mark-activity.js +82 -75
  66. package/dist/permission-selection.js +27 -17
  67. package/dist/popover.js +138 -130
  68. package/dist/screens.d.ts +20 -3
  69. package/dist/screens.js +275 -6
  70. package/dist/select-model.js +34 -28
  71. package/dist/text-diff.d.ts +1 -0
  72. package/dist/text-diff.js +106 -74
  73. package/dist/theme.js +34 -40
  74. package/package.json +56 -7
package/dist/Feedback.js CHANGED
@@ -1,247 +1,781 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { i18n as _i18n } from "@lingui/core";
3
+ import { useLocaleRevision } from "./i18n.js";
2
4
  import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
3
5
  import { FeedbackMarkup } from "./FeedbackMarkup.js";
4
6
  import { FeedbackVerification } from "./FeedbackVerification.js";
5
7
  import { FEEDBACK_ERROR_CODES, FEEDBACK_MAX_MESSAGE_LENGTH, captureFeedbackScreenshot, feedbackDiagnostics, feedbackEvents, feedbackTopics, readFeedbackScreenshot, resolveFeedbackEndpoint, screenshotUrl, sendFeedback } from "./feedback-data.js";
6
8
  export { feedbackApplication, resolveFeedbackEndpoint } from "./feedback-data.js";
7
9
  const FeedbackContext = createContext(null);
8
- const disabledFeedback = { enabled: false, open() { }, reportError() { }, wiggle: 0 };
9
- export function useFeedback() { return useContext(FeedbackContext) ?? disabledFeedback; }
10
- export function FeedbackBug({ wiggle = 0 }) {
11
- return _jsxs("svg", { className: `mdbase-feedback-bug${wiggle ? " is-wiggling" : ""}`, viewBox: "0 0 48 48", "aria-hidden": "true", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M16 23 8 19l-3 2m10 8H6l-2 3m12 3-7 4v3m23-19 8-4 3 2m-10 8h9l2 3m-12 3 7 4v3M20 13l-4-6-4 1m16 5 4-6 4 1" }), _jsx("ellipse", { cx: "24", cy: "29", rx: "10", ry: "12" }), _jsx("ellipse", { cx: "24", cy: "16", rx: "7", ry: "6" }), _jsx("path", { d: "M24 22v17" }), _jsx("circle", { cx: "21", cy: "15", r: "1.5", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "27", cy: "15", r: "1.5", fill: "currentColor", stroke: "none" })] }, wiggle);
10
+ const disabledFeedback = {
11
+ enabled: false,
12
+ open() {},
13
+ reportError() {},
14
+ wiggle: 0
15
+ };
16
+ export function useFeedback() {
17
+ return useContext(FeedbackContext) ?? disabledFeedback;
12
18
  }
13
- export function FeedbackButton({ className, topic }) {
14
- const feedback = useFeedback();
15
- if (!feedback.enabled)
16
- return null;
17
- return _jsxs("button", { type: "button", className: ["mdbase-feedback-trigger", className].filter(Boolean).join(" "), onClick: () => feedback.open(topic), children: [_jsx(FeedbackBug, { wiggle: feedback.wiggle }), _jsx("span", { children: topic === "problem" ? "Report a problem" : "Send feedback" })] });
19
+ export function FeedbackBug({
20
+ wiggle = 0
21
+ }) {
22
+ useLocaleRevision();
23
+ return _jsxs("svg", {
24
+ className: `mdbase-feedback-bug${wiggle ? " is-wiggling" : ""}`,
25
+ viewBox: "0 0 48 48",
26
+ "aria-hidden": "true",
27
+ fill: "none",
28
+ stroke: "currentColor",
29
+ strokeWidth: "1.8",
30
+ strokeLinecap: "round",
31
+ strokeLinejoin: "round",
32
+ children: [_jsx("path", {
33
+ d: "M16 23 8 19l-3 2m10 8H6l-2 3m12 3-7 4v3m23-19 8-4 3 2m-10 8h9l2 3m-12 3 7 4v3M20 13l-4-6-4 1m16 5 4-6 4 1"
34
+ }), _jsx("ellipse", {
35
+ cx: "24",
36
+ cy: "29",
37
+ rx: "10",
38
+ ry: "12"
39
+ }), _jsx("ellipse", {
40
+ cx: "24",
41
+ cy: "16",
42
+ rx: "7",
43
+ ry: "6"
44
+ }), _jsx("path", {
45
+ d: "M24 22v17"
46
+ }), _jsx("circle", {
47
+ cx: "21",
48
+ cy: "15",
49
+ r: "1.5",
50
+ fill: "currentColor",
51
+ stroke: "none"
52
+ }), _jsx("circle", {
53
+ cx: "27",
54
+ cy: "15",
55
+ r: "1.5",
56
+ fill: "currentColor",
57
+ stroke: "none"
58
+ })]
59
+ }, wiggle);
60
+ }
61
+ export function FeedbackButton({
62
+ className,
63
+ topic
64
+ }) {
65
+ useLocaleRevision();
66
+ const feedback = useFeedback();
67
+ if (!feedback.enabled) return null;
68
+ return _jsxs("button", {
69
+ type: "button",
70
+ className: ["mdbase-feedback-trigger", className].filter(Boolean).join(" "),
71
+ onClick: () => feedback.open(topic),
72
+ children: [_jsx(FeedbackBug, {
73
+ wiggle: feedback.wiggle
74
+ }), _jsx("span", {
75
+ children: topic === "problem" ? _i18n._(
76
+ /** i18n */
77
+ {
78
+ id: "hV3yAb",
79
+ message: "Report a problem"
80
+ }) : _i18n._(
81
+ /** i18n */
82
+ {
83
+ id: "RoafuO",
84
+ message: "Send feedback"
85
+ })
86
+ })]
87
+ });
18
88
  }
19
89
  /** One per application shell. No Connect/auth dependency; drafts live only in this mounted provider. */
20
- export function FeedbackProvider({ endpoint: configuredEndpoint, application, turnstileSiteKey, collectionName, children }) {
21
- const endpoint = resolveFeedbackEndpoint(configuredEndpoint ?? undefined);
22
- const dialog = useRef(null);
23
- const content = useRef(null);
24
- const messageField = useRef(null);
25
- const successHeading = useRef(null);
26
- const body = useRef(null);
27
- const trigger = useRef(null);
28
- const events = useRef([]);
29
- const lastWiggle = useRef(-Infinity);
30
- const imageOperation = useRef(null);
31
- const submissionOperation = useRef(null);
32
- const attempt = useRef(null);
33
- const live = useRef(true);
34
- const [open, setOpen] = useState(false);
35
- const [topic, setTopic] = useState("problem");
36
- const [message, setMessage] = useState("");
37
- const [replyEmail, setReplyEmail] = useState("");
38
- const [includeCollection, setIncludeCollection] = useState(false);
39
- const [includeDiagnostics, setIncludeDiagnostics] = useState(false);
40
- const [diagnostics, setDiagnostics] = useState(null);
41
- const [screenshot, setScreenshot] = useState(null);
42
- const [includeScreenshot, setIncludeScreenshot] = useState(true);
43
- const [markup, setMarkup] = useState(false);
44
- const [imageBusy, setImageBusy] = useState(false);
45
- const [capturing, setCapturing] = useState(false);
46
- const [captureStatus, setCaptureStatus] = useState("");
47
- const [screenshotError, setScreenshotError] = useState("");
48
- const [submitError, setSubmitError] = useState("");
49
- const [submitting, setSubmitting] = useState(false);
50
- const [submitted, setSubmitted] = useState(null);
51
- const [turnstileToken, setTurnstileToken] = useState("");
52
- const [verificationAttempt, setVerificationAttempt] = useState(0);
53
- const [wiggle, setWiggle] = useState(0);
54
- const id = useId();
55
- useEffect(() => {
56
- live.current = true;
57
- return () => { live.current = false; imageOperation.current?.abort(); submissionOperation.current?.abort(); if (content.current)
58
- content.current.inert = false; };
59
- }, []);
60
- useLayoutEffect(() => { setIncludeCollection(false); }, [collectionName]);
61
- useEffect(() => {
62
- if (!wiggle)
63
- return;
64
- const timer = setTimeout(() => setWiggle(0), 600);
65
- return () => clearTimeout(timer);
66
- }, [wiggle]);
67
- const openFeedback = useCallback((next) => {
68
- if (!endpoint)
69
- return;
70
- trigger.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
71
- if (next)
72
- setTopic(next);
73
- setDiagnostics(feedbackDiagnostics(events.current));
74
- setOpen(true);
75
- }, [endpoint]);
76
- const reportError = useCallback((failure) => {
77
- if (!endpoint || failure.code === "cancelled")
78
- return;
79
- if (!FEEDBACK_ERROR_CODES.includes(failure.code) || (failure.status !== undefined && (!Number.isInteger(failure.status) || failure.status < 100 || failure.status > 599)))
80
- throw new TypeError("Feedback failures must contain a bounded error code and HTTP status only.");
81
- const event = { at: new Date().toISOString(), code: failure.code, ...(failure.status === undefined ? {} : { status: failure.status }) };
82
- events.current = feedbackEvents([...events.current, event]);
83
- if (document.visibilityState === "visible" && performance.now() - lastWiggle.current >= 30_000) {
84
- lastWiggle.current = performance.now();
85
- setWiggle((value) => value + 1);
86
- }
87
- }, [endpoint]);
88
- const controls = useMemo(() => ({ enabled: Boolean(endpoint), open: openFeedback, reportError, wiggle }), [endpoint, openFeedback, reportError, wiggle]);
89
- useLayoutEffect(() => {
90
- if (!dialog.current)
91
- return;
92
- if (open && !capturing) {
93
- if (!dialog.current.open)
94
- dialog.current.showModal();
95
- if (submitted)
96
- successHeading.current?.focus({ preventScroll: true });
97
- else
98
- messageField.current?.focus({ preventScroll: true });
99
- }
100
- else if (dialog.current.open)
101
- dialog.current.close();
102
- }, [open, capturing, submitted]);
103
- function resetDraft() {
104
- setTopic("problem");
105
- setMessage("");
106
- setReplyEmail("");
107
- setIncludeCollection(false);
108
- setIncludeDiagnostics(false);
109
- setScreenshot(null);
110
- setIncludeScreenshot(true);
111
- setSubmitted(null);
112
- setSubmitError("");
113
- setScreenshotError("");
114
- setCaptureStatus("");
115
- attempt.current = null;
90
+ export function FeedbackProvider({
91
+ endpoint: configuredEndpoint,
92
+ application,
93
+ turnstileSiteKey,
94
+ collectionName,
95
+ children
96
+ }) {
97
+ useLocaleRevision();
98
+ const endpoint = resolveFeedbackEndpoint(configuredEndpoint ?? undefined);
99
+ const dialog = useRef(null);
100
+ const content = useRef(null);
101
+ const messageField = useRef(null);
102
+ const successHeading = useRef(null);
103
+ const body = useRef(null);
104
+ const trigger = useRef(null);
105
+ const events = useRef([]);
106
+ const lastWiggle = useRef(-Infinity);
107
+ const imageOperation = useRef(null);
108
+ const submissionOperation = useRef(null);
109
+ const attempt = useRef(null);
110
+ const live = useRef(true);
111
+ const [open, setOpen] = useState(false);
112
+ const [topic, setTopic] = useState("problem");
113
+ const [message, setMessage] = useState("");
114
+ const [replyEmail, setReplyEmail] = useState("");
115
+ const [includeCollection, setIncludeCollection] = useState(false);
116
+ const [includeDiagnostics, setIncludeDiagnostics] = useState(false);
117
+ const [diagnostics, setDiagnostics] = useState(null);
118
+ const [screenshot, setScreenshot] = useState(null);
119
+ const [includeScreenshot, setIncludeScreenshot] = useState(true);
120
+ const [markup, setMarkup] = useState(false);
121
+ const [imageBusy, setImageBusy] = useState(false);
122
+ const [capturing, setCapturing] = useState(false);
123
+ const [captureStatus, setCaptureStatus] = useState("");
124
+ const [screenshotError, setScreenshotError] = useState("");
125
+ const [submitError, setSubmitError] = useState("");
126
+ const [submitting, setSubmitting] = useState(false);
127
+ const [submitted, setSubmitted] = useState(null);
128
+ const [turnstileToken, setTurnstileToken] = useState("");
129
+ const [verificationAttempt, setVerificationAttempt] = useState(0);
130
+ const [wiggle, setWiggle] = useState(0);
131
+ const id = useId();
132
+ useEffect(() => {
133
+ live.current = true;
134
+ return () => {
135
+ live.current = false;
136
+ imageOperation.current?.abort();
137
+ submissionOperation.current?.abort();
138
+ if (content.current) content.current.inert = false;
139
+ };
140
+ }, []);
141
+ useLayoutEffect(() => {
142
+ setIncludeCollection(false);
143
+ }, [collectionName]);
144
+ useEffect(() => {
145
+ if (!wiggle) return;
146
+ const timer = setTimeout(() => setWiggle(0), 600);
147
+ return () => clearTimeout(timer);
148
+ }, [wiggle]);
149
+ const openFeedback = useCallback(next => {
150
+ if (!endpoint) return;
151
+ trigger.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
152
+ if (next) setTopic(next);
153
+ setDiagnostics(feedbackDiagnostics(events.current));
154
+ setOpen(true);
155
+ }, [endpoint]);
156
+ const reportError = useCallback(failure => {
157
+ if (!endpoint || failure.code === "cancelled") return;
158
+ if (!FEEDBACK_ERROR_CODES.includes(failure.code) || failure.status !== undefined && (!Number.isInteger(failure.status) || failure.status < 100 || failure.status > 599)) throw new TypeError("Feedback failures must contain a bounded error code and HTTP status only.");
159
+ const event = {
160
+ at: new Date().toISOString(),
161
+ code: failure.code,
162
+ ...(failure.status === undefined ? {} : {
163
+ status: failure.status
164
+ })
165
+ };
166
+ events.current = feedbackEvents([...events.current, event]);
167
+ if (document.visibilityState === "visible" && performance.now() - lastWiggle.current >= 30_000) {
168
+ lastWiggle.current = performance.now();
169
+ setWiggle(value => value + 1);
116
170
  }
117
- function close() {
118
- if (submitting)
119
- return;
120
- imageOperation.current?.abort();
121
- imageOperation.current = null;
171
+ }, [endpoint]);
172
+ const controls = useMemo(() => ({
173
+ enabled: Boolean(endpoint),
174
+ open: openFeedback,
175
+ reportError,
176
+ wiggle
177
+ }), [endpoint, openFeedback, reportError, wiggle]);
178
+ useLayoutEffect(() => {
179
+ if (!dialog.current) return;
180
+ if (open && !capturing) {
181
+ if (!dialog.current.open) dialog.current.showModal();
182
+ if (submitted) successHeading.current?.focus({
183
+ preventScroll: true
184
+ });else messageField.current?.focus({
185
+ preventScroll: true
186
+ });
187
+ } else if (dialog.current.open) dialog.current.close();
188
+ }, [open, capturing, submitted]);
189
+ function resetDraft() {
190
+ setTopic("problem");
191
+ setMessage("");
192
+ setReplyEmail("");
193
+ setIncludeCollection(false);
194
+ setIncludeDiagnostics(false);
195
+ setScreenshot(null);
196
+ setIncludeScreenshot(true);
197
+ setSubmitted(null);
198
+ setSubmitError("");
199
+ setScreenshotError("");
200
+ setCaptureStatus("");
201
+ attempt.current = null;
202
+ }
203
+ function close() {
204
+ if (submitting) return;
205
+ imageOperation.current?.abort();
206
+ imageOperation.current = null;
207
+ setCapturing(false);
208
+ setImageBusy(false);
209
+ setMarkup(false);
210
+ setOpen(false);
211
+ setTurnstileToken("");
212
+ setVerificationAttempt(value => value + 1);
213
+ if (submitted) resetDraft();
214
+ dialog.current?.close();
215
+ trigger.current?.focus({
216
+ preventScroll: true
217
+ });
218
+ }
219
+ async function capture() {
220
+ if (imageOperation.current || submitting) return;
221
+ const controller = new AbortController();
222
+ imageOperation.current = controller;
223
+ const scroll = body.current?.scrollTop ?? 0;
224
+ setImageBusy(true);
225
+ setCapturing(true);
226
+ setScreenshotError("");
227
+ setCaptureStatus("");
228
+ // Close synchronously, before either the picker or capture stream sees this native modal.
229
+ dialog.current?.close();
230
+ if (content.current) content.current.inert = true;
231
+ try {
232
+ const next = await captureFeedbackScreenshot(controller.signal);
233
+ if (!controller.signal.aborted && live.current) {
234
+ setScreenshot(next);
235
+ setIncludeScreenshot(true);
236
+ setCaptureStatus(_i18n._(
237
+ /** i18n */
238
+ {
239
+ id: "25QKdy",
240
+ message: "Screenshot captured. Sharing has stopped."
241
+ }));
242
+ }
243
+ } catch (reason) {
244
+ if (!controller.signal.aborted && live.current) {
245
+ if (reason instanceof DOMException && (reason.name === "NotAllowedError" || reason.name === "AbortError")) setCaptureStatus(_i18n._(
246
+ /** i18n */
247
+ {
248
+ id: "EfTCex",
249
+ message: "No screenshot taken. You can still send feedback."
250
+ }));else setScreenshotError(reason instanceof Error ? reason.message : _i18n._(
251
+ /** i18n */
252
+ {
253
+ id: "6vUPNB",
254
+ message: "The screenshot could not be captured."
255
+ }));
256
+ }
257
+ } finally {
258
+ if (imageOperation.current === controller) imageOperation.current = null;
259
+ if (content.current) content.current.inert = false;
260
+ if (!controller.signal.aborted && live.current) {
122
261
  setCapturing(false);
123
262
  setImageBusy(false);
124
- setMarkup(false);
125
- setOpen(false);
126
- setTurnstileToken("");
127
- setVerificationAttempt((value) => value + 1);
128
- if (submitted)
129
- resetDraft();
130
- dialog.current?.close();
131
- trigger.current?.focus({ preventScroll: true });
132
- }
133
- async function capture() {
134
- if (imageOperation.current || submitting)
135
- return;
136
- const controller = new AbortController();
137
- imageOperation.current = controller;
138
- const scroll = body.current?.scrollTop ?? 0;
139
- setImageBusy(true);
140
- setCapturing(true);
141
- setScreenshotError("");
142
- setCaptureStatus("");
143
- // Close synchronously, before either the picker or capture stream sees this native modal.
144
- dialog.current?.close();
145
- if (content.current)
146
- content.current.inert = true;
147
- try {
148
- const next = await captureFeedbackScreenshot(controller.signal);
149
- if (!controller.signal.aborted && live.current) {
150
- setScreenshot(next);
151
- setIncludeScreenshot(true);
152
- setCaptureStatus("Screenshot captured. Sharing has stopped.");
153
- }
154
- }
155
- catch (reason) {
156
- if (!controller.signal.aborted && live.current) {
157
- if (reason instanceof DOMException && (reason.name === "NotAllowedError" || reason.name === "AbortError"))
158
- setCaptureStatus("No screenshot taken. You can still send feedback.");
159
- else
160
- setScreenshotError(reason instanceof Error ? reason.message : "The screenshot could not be captured.");
161
- }
162
- }
163
- finally {
164
- if (imageOperation.current === controller)
165
- imageOperation.current = null;
166
- if (content.current)
167
- content.current.inert = false;
168
- if (!controller.signal.aborted && live.current) {
169
- setCapturing(false);
170
- setImageBusy(false);
171
- if (dialog.current && !dialog.current.open)
172
- dialog.current.showModal();
173
- if (body.current)
174
- body.current.scrollTop = scroll;
175
- }
176
- }
263
+ if (dialog.current && !dialog.current.open) dialog.current.showModal();
264
+ if (body.current) body.current.scrollTop = scroll;
265
+ }
177
266
  }
178
- async function upload(file) {
179
- if (imageOperation.current || submitting)
180
- return;
181
- const controller = new AbortController();
182
- imageOperation.current = controller;
183
- setImageBusy(true);
184
- setScreenshotError("");
185
- setCaptureStatus("");
186
- try {
187
- const next = await readFeedbackScreenshot(file);
188
- if (!controller.signal.aborted && live.current) {
189
- setScreenshot(next);
190
- setIncludeScreenshot(true);
191
- }
192
- }
193
- catch (reason) {
194
- if (!controller.signal.aborted && live.current)
195
- setScreenshotError(reason instanceof Error ? reason.message : "The screenshot could not be read.");
196
- }
197
- finally {
198
- if (imageOperation.current === controller)
199
- imageOperation.current = null;
200
- if (!controller.signal.aborted && live.current)
201
- setImageBusy(false);
202
- }
267
+ }
268
+ async function upload(file) {
269
+ if (imageOperation.current || submitting) return;
270
+ const controller = new AbortController();
271
+ imageOperation.current = controller;
272
+ setImageBusy(true);
273
+ setScreenshotError("");
274
+ setCaptureStatus("");
275
+ try {
276
+ const next = await readFeedbackScreenshot(file);
277
+ if (!controller.signal.aborted && live.current) {
278
+ setScreenshot(next);
279
+ setIncludeScreenshot(true);
280
+ }
281
+ } catch (reason) {
282
+ if (!controller.signal.aborted && live.current) setScreenshotError(reason instanceof Error ? reason.message : _i18n._(
283
+ /** i18n */
284
+ {
285
+ id: "9mrfxN",
286
+ message: "The screenshot could not be read."
287
+ }));
288
+ } finally {
289
+ if (imageOperation.current === controller) imageOperation.current = null;
290
+ if (!controller.signal.aborted && live.current) setImageBusy(false);
203
291
  }
204
- async function submit() {
205
- if (!endpoint || !message.trim() || imageOperation.current || submissionOperation.current || (turnstileSiteKey && !turnstileToken))
206
- return;
207
- const payload = {
208
- schema_version: 2, application, topic, message: message.trim(),
209
- ...(replyEmail.trim() ? { reply_email: replyEmail.trim() } : {}),
210
- ...(includeCollection && collectionName ? { context: { collection_name: collectionName } } : {}),
211
- ...(topic === "problem" && includeDiagnostics && diagnostics ? { diagnostics } : {}),
212
- ...(screenshot && includeScreenshot ? { screenshot } : {})
213
- };
214
- const fingerprint = JSON.stringify(payload);
215
- if (attempt.current?.fingerprint !== fingerprint)
216
- attempt.current = { fingerprint, requestId: crypto.randomUUID() };
217
- const submission = { ...payload, request_id: attempt.current.requestId, ...(turnstileToken ? { turnstile_token: turnstileToken } : {}) };
218
- const controller = new AbortController();
219
- submissionOperation.current = controller;
220
- const timer = setTimeout(() => controller.abort(), 20_000);
221
- setSubmitting(true);
222
- setSubmitError("");
223
- try {
224
- await sendFeedback(endpoint, submission, controller.signal);
225
- if (live.current) {
226
- setSubmitted(topic);
227
- setScreenshot(null);
228
- }
229
- }
230
- catch (reason) {
231
- if (live.current)
232
- setSubmitError(controller.signal.aborted ? "Sending timed out. Please try again." : reason instanceof Error ? reason.message : "Feedback could not be sent. Please try again.");
233
- }
234
- finally {
235
- clearTimeout(timer);
236
- submissionOperation.current = null;
237
- if (live.current) {
238
- setSubmitting(false);
239
- setTurnstileToken("");
240
- setVerificationAttempt((value) => value + 1);
241
- }
292
+ }
293
+ async function submit() {
294
+ if (!endpoint || !message.trim() || imageOperation.current || submissionOperation.current || turnstileSiteKey && !turnstileToken) return;
295
+ const payload = {
296
+ schema_version: 2,
297
+ application,
298
+ topic,
299
+ message: message.trim(),
300
+ ...(replyEmail.trim() ? {
301
+ reply_email: replyEmail.trim()
302
+ } : {}),
303
+ ...(includeCollection && collectionName ? {
304
+ context: {
305
+ collection_name: collectionName
242
306
  }
307
+ } : {}),
308
+ ...(topic === "problem" && includeDiagnostics && diagnostics ? {
309
+ diagnostics
310
+ } : {}),
311
+ ...(screenshot && includeScreenshot ? {
312
+ screenshot
313
+ } : {})
314
+ };
315
+ const fingerprint = JSON.stringify(payload);
316
+ if (attempt.current?.fingerprint !== fingerprint) attempt.current = {
317
+ fingerprint,
318
+ requestId: crypto.randomUUID()
319
+ };
320
+ const submission = {
321
+ ...payload,
322
+ request_id: attempt.current.requestId,
323
+ ...(turnstileToken ? {
324
+ turnstile_token: turnstileToken
325
+ } : {})
326
+ };
327
+ const controller = new AbortController();
328
+ submissionOperation.current = controller;
329
+ const timer = setTimeout(() => controller.abort(), 20_000);
330
+ setSubmitting(true);
331
+ setSubmitError("");
332
+ try {
333
+ await sendFeedback(endpoint, submission, controller.signal);
334
+ if (live.current) {
335
+ setSubmitted(topic);
336
+ setScreenshot(null);
337
+ }
338
+ } catch (reason) {
339
+ if (live.current) setSubmitError(controller.signal.aborted ? _i18n._(
340
+ /** i18n */
341
+ {
342
+ id: "OQBR5K",
343
+ message: "Sending timed out. Please try again."
344
+ }) : reason instanceof Error ? reason.message : _i18n._(
345
+ /** i18n */
346
+ {
347
+ id: "mRd7qL",
348
+ message: "Feedback could not be sent. Please try again."
349
+ }));
350
+ } finally {
351
+ clearTimeout(timer);
352
+ submissionOperation.current = null;
353
+ if (live.current) {
354
+ setSubmitting(false);
355
+ setTurnstileToken("");
356
+ setVerificationAttempt(value => value + 1);
357
+ }
243
358
  }
244
- const config = feedbackTopics[topic];
245
- return _jsxs(FeedbackContext.Provider, { value: controls, children: [_jsx("div", { className: "mdbase-feedback-app", ref: content, children: children }), _jsx("span", { className: "mdbase-feedback-sr-only", role: "status", children: wiggle ? "A problem occurred. You can use Send feedback to report it." : "" }), endpoint && _jsxs("dialog", { ref: dialog, className: "mdbase-feedback-dialog", "aria-labelledby": `${id}-title`, onKeyDown: (event) => event.stopPropagation(), onCancel: (event) => { event.preventDefault(); close(); }, children: [_jsxs("header", { className: "mdbase-feedback-header", children: [_jsx("h2", { id: `${id}-title`, children: "Send feedback" }), _jsx("button", { type: "button", disabled: submitting, onClick: close, "aria-label": "Close feedback", children: "\u00D7" })] }), submitted ? _jsxs("section", { className: "mdbase-feedback-success", children: [_jsx("h3", { ref: successHeading, tabIndex: -1, children: feedbackTopics[submitted].thanks }), _jsx("p", { children: "Sent privately to the mdbase team." }), _jsx("button", { type: "button", className: "mdbase-feedback-primary", onClick: close, children: "Back to your work" })] }) : _jsxs("form", { onSubmit: (event) => { event.preventDefault(); void submit(); }, children: [_jsxs("div", { className: "mdbase-feedback-body", ref: body, children: [_jsxs("fieldset", { className: "mdbase-feedback-topics", disabled: submitting, children: [_jsx("legend", { className: "mdbase-feedback-sr-only", children: "Feedback type" }), Object.keys(feedbackTopics).map((value) => _jsxs("label", { children: [_jsx("input", { type: "radio", name: `${id}-topic`, checked: topic === value, onChange: () => setTopic(value) }), feedbackTopics[value].label] }, value))] }), _jsxs("label", { className: "mdbase-feedback-field", children: [_jsx("span", { children: config.prompt }), _jsx("textarea", { ref: messageField, required: true, maxLength: FEEDBACK_MAX_MESSAGE_LENGTH, rows: 5, placeholder: config.placeholder, value: message, disabled: submitting, onChange: (event) => setMessage(event.target.value) })] }), _jsxs("label", { className: "mdbase-feedback-field", children: [_jsxs("span", { children: ["Reply email ", _jsx("small", { children: "Optional" })] }), _jsx("input", { type: "email", maxLength: 320, autoComplete: "email", placeholder: "you@example.com", value: replyEmail, disabled: submitting, onChange: (event) => setReplyEmail(event.target.value) })] }), _jsxs("div", { className: "mdbase-feedback-screenshot", role: "group", "aria-label": "Screenshot", children: [screenshot && _jsxs(_Fragment, { children: [_jsxs("button", { className: "mdbase-feedback-preview", type: "button", disabled: imageBusy || submitting, onClick: () => { setIncludeScreenshot(false); setMarkup(true); }, "aria-label": "Mark up screenshot", children: [_jsx("img", { src: screenshotUrl(screenshot), alt: "Screenshot attached to your feedback" }), _jsx("span", { children: "Mark up screenshot" })] }), _jsxs("div", { className: "mdbase-feedback-screenshot-actions", children: [_jsxs("label", { children: [_jsx("input", { className: "mdbase-checkbox", type: "checkbox", checked: includeScreenshot, disabled: submitting, onChange: (event) => setIncludeScreenshot(event.target.checked) }), "Include screenshot"] }), _jsx("button", { type: "button", disabled: imageBusy || submitting, onClick: () => { setScreenshot(null); setScreenshotError(""); setCaptureStatus(""); }, children: "Remove" })] })] }), _jsxs("div", { className: "mdbase-feedback-screenshot-actions", children: [_jsx("button", { type: "button", disabled: imageBusy || submitting, onClick: () => void capture(), children: capturing ? "Taking screenshot…" : screenshot ? "Retake screenshot" : "Attach screenshot" }), _jsxs("label", { className: "mdbase-feedback-upload", children: [_jsx("span", { children: "Choose an image" }), _jsx("input", { type: "file", accept: "image/png,image/jpeg", disabled: imageBusy || submitting, onChange: (event) => { const file = event.target.files?.[0]; event.target.value = ""; if (file)
246
- void upload(file); } })] })] }), screenshot && _jsx("p", { className: "mdbase-feedback-help", children: "Review for private information before sending." }), captureStatus && _jsx("p", { className: "mdbase-feedback-help", role: "status", children: captureStatus }), screenshotError && _jsx("p", { className: "mdbase-feedback-error", role: "alert", children: screenshotError })] }), _jsxs("details", { className: "mdbase-feedback-details", children: [_jsx("summary", { children: "What gets sent" }), _jsx("p", { className: "mdbase-feedback-help", children: "Your message, any reply email and included screenshot, plus these app and build details." }), _jsx("pre", { children: JSON.stringify(application, null, 2) }), collectionName && _jsxs("label", { className: "mdbase-feedback-check", children: [_jsx("input", { className: "mdbase-checkbox", type: "checkbox", checked: includeCollection, disabled: submitting, onChange: (event) => setIncludeCollection(event.target.checked) }), "Include collection name: ", collectionName] }), topic === "problem" && _jsxs(_Fragment, { children: [_jsxs("label", { className: "mdbase-feedback-check", children: [_jsx("input", { className: "mdbase-checkbox", type: "checkbox", checked: includeDiagnostics, disabled: submitting, onChange: (event) => setIncludeDiagnostics(event.target.checked) }), "Include technical diagnostics"] }), _jsx("p", { className: "mdbase-feedback-help", children: "Browser, OS, viewport and recent error codes. No note contents, paths or credentials." }), includeDiagnostics && _jsx("pre", { children: JSON.stringify(diagnostics, null, 2) })] }), _jsx("p", { className: "mdbase-feedback-help", children: "Images: PNG or JPEG, up to 3 MB. Blackout replaces pixels; only the final image is attached." }), _jsx("p", { className: "mdbase-feedback-help", children: "Replies aren\u2019t guaranteed. Not for security reports or urgent support." })] }), turnstileSiteKey && open && !capturing && _jsx(FeedbackVerification, { siteKey: turnstileSiteKey, onToken: setTurnstileToken }, verificationAttempt), submitError && _jsx("p", { className: "mdbase-feedback-error", role: "alert", children: submitError }), _jsx("p", { className: "mdbase-feedback-help", children: "Private feedback to the mdbase team." })] }), _jsxs("footer", { className: "mdbase-feedback-actions", children: [_jsx("button", { type: "button", disabled: submitting, onClick: close, children: "Cancel" }), _jsx("button", { type: "submit", className: "mdbase-feedback-primary", disabled: submitting || imageBusy || !message.trim() || Boolean(turnstileSiteKey && !turnstileToken), children: submitting ? "Sending…" : "Send feedback" })] })] })] }), markup && screenshot && _jsx(FeedbackMarkup, { screenshot: screenshot, onClose: () => { setMarkup(false); setCaptureStatus("Markup discarded. Screenshot excluded."); }, onApply: (next) => { setScreenshot(next); setIncludeScreenshot(true); setMarkup(false); setCaptureStatus("Markup applied."); } })] });
359
+ }
360
+ const config = feedbackTopics[topic];
361
+ return _jsxs(FeedbackContext.Provider, {
362
+ value: controls,
363
+ children: [_jsx("div", {
364
+ className: "mdbase-feedback-app",
365
+ ref: content,
366
+ children: children
367
+ }), _jsx("span", {
368
+ className: "mdbase-feedback-sr-only",
369
+ role: "status",
370
+ children: wiggle ? _i18n._(
371
+ /** i18n */
372
+ {
373
+ id: "7qrUoz",
374
+ message: "A problem occurred. You can use Send feedback to report it."
375
+ }) : ""
376
+ }), endpoint && _jsxs("dialog", {
377
+ ref: dialog,
378
+ className: "mdbase-feedback-dialog",
379
+ "aria-labelledby": `${id}-title`,
380
+ onKeyDown: event => event.stopPropagation(),
381
+ onCancel: event => {
382
+ event.preventDefault();
383
+ close();
384
+ },
385
+ children: [_jsxs("header", {
386
+ className: "mdbase-feedback-header",
387
+ children: [_jsx("h2", {
388
+ id: `${id}-title`,
389
+ children: _i18n._(
390
+ /** i18n */
391
+ {
392
+ id: "RoafuO",
393
+ message: "Send feedback"
394
+ })
395
+ }), _jsx("button", {
396
+ type: "button",
397
+ disabled: submitting,
398
+ onClick: close,
399
+ "aria-label": _i18n._(
400
+ /** i18n */
401
+ {
402
+ id: "lnj9gP",
403
+ message: "Close feedback"
404
+ }),
405
+ children: "\u00D7"
406
+ })]
407
+ }), submitted ? _jsxs("section", {
408
+ className: "mdbase-feedback-success",
409
+ children: [_jsx("h3", {
410
+ ref: successHeading,
411
+ tabIndex: -1,
412
+ children: feedbackTopics[submitted].thanks
413
+ }), _jsx("p", {
414
+ children: _i18n._(
415
+ /** i18n */
416
+ {
417
+ id: "tHdrmM",
418
+ message: "Sent privately to the mdbase team."
419
+ })
420
+ }), _jsx("button", {
421
+ type: "button",
422
+ className: "mdbase-feedback-primary",
423
+ onClick: close,
424
+ children: _i18n._(
425
+ /** i18n */
426
+ {
427
+ id: "9yK4bJ",
428
+ message: "Back to your work"
429
+ })
430
+ })]
431
+ }) : _jsxs("form", {
432
+ onSubmit: event => {
433
+ event.preventDefault();
434
+ void submit();
435
+ },
436
+ children: [_jsxs("div", {
437
+ className: "mdbase-feedback-body",
438
+ ref: body,
439
+ children: [_jsxs("fieldset", {
440
+ className: "mdbase-feedback-topics",
441
+ disabled: submitting,
442
+ children: [_jsx("legend", {
443
+ className: "mdbase-feedback-sr-only",
444
+ children: _i18n._(
445
+ /** i18n */
446
+ {
447
+ id: "88u4WE",
448
+ message: "Feedback type"
449
+ })
450
+ }), Object.keys(feedbackTopics).map(value => _jsxs("label", {
451
+ children: [_jsx("input", {
452
+ type: "radio",
453
+ name: `${id}-topic`,
454
+ checked: topic === value,
455
+ onChange: () => setTopic(value)
456
+ }), feedbackTopics[value].label]
457
+ }, value))]
458
+ }), _jsxs("label", {
459
+ className: "mdbase-feedback-field",
460
+ children: [_jsx("span", {
461
+ children: config.prompt
462
+ }), _jsx("textarea", {
463
+ ref: messageField,
464
+ required: true,
465
+ maxLength: FEEDBACK_MAX_MESSAGE_LENGTH,
466
+ rows: 5,
467
+ placeholder: config.placeholder,
468
+ value: message,
469
+ disabled: submitting,
470
+ onChange: event => setMessage(event.target.value)
471
+ })]
472
+ }), _jsxs("label", {
473
+ className: "mdbase-feedback-field",
474
+ children: [_jsxs("span", {
475
+ children: [_i18n._(
476
+ /** i18n */
477
+ {
478
+ id: "zVYLbq",
479
+ message: "Reply email"
480
+ }), " ", _jsx("small", {
481
+ children: _i18n._(
482
+ /** i18n */
483
+ {
484
+ id: "LLAa_9",
485
+ message: "Optional"
486
+ })
487
+ })]
488
+ }), _jsx("input", {
489
+ type: "email",
490
+ maxLength: 320,
491
+ autoComplete: "email",
492
+ placeholder: _i18n._(
493
+ /** i18n */
494
+ {
495
+ id: "njBr8W",
496
+ message: "you@example.com"
497
+ }),
498
+ value: replyEmail,
499
+ disabled: submitting,
500
+ onChange: event => setReplyEmail(event.target.value)
501
+ })]
502
+ }), _jsxs("div", {
503
+ className: "mdbase-feedback-screenshot",
504
+ role: "group",
505
+ "aria-label": _i18n._(
506
+ /** i18n */
507
+ {
508
+ id: "IxyZXD",
509
+ message: "Screenshot"
510
+ }),
511
+ children: [screenshot && _jsxs(_Fragment, {
512
+ children: [_jsxs("button", {
513
+ className: "mdbase-feedback-preview",
514
+ type: "button",
515
+ disabled: imageBusy || submitting,
516
+ onClick: () => {
517
+ setIncludeScreenshot(false);
518
+ setMarkup(true);
519
+ },
520
+ "aria-label": _i18n._(
521
+ /** i18n */
522
+ {
523
+ id: "wsx-_Y",
524
+ message: "Mark up screenshot"
525
+ }),
526
+ children: [_jsx("img", {
527
+ src: screenshotUrl(screenshot),
528
+ alt: _i18n._(
529
+ /** i18n */
530
+ {
531
+ id: "Nj4zfV",
532
+ message: "Screenshot attached to your feedback"
533
+ })
534
+ }), _jsx("span", {
535
+ children: _i18n._(
536
+ /** i18n */
537
+ {
538
+ id: "wsx-_Y",
539
+ message: "Mark up screenshot"
540
+ })
541
+ })]
542
+ }), _jsxs("div", {
543
+ className: "mdbase-feedback-screenshot-actions",
544
+ children: [_jsxs("label", {
545
+ children: [_jsx("input", {
546
+ className: "mdbase-checkbox",
547
+ type: "checkbox",
548
+ checked: includeScreenshot,
549
+ disabled: submitting,
550
+ onChange: event => setIncludeScreenshot(event.target.checked)
551
+ }), _i18n._(
552
+ /** i18n */
553
+ {
554
+ id: "GnOlBF",
555
+ message: "Include screenshot"
556
+ })]
557
+ }), _jsx("button", {
558
+ type: "button",
559
+ disabled: imageBusy || submitting,
560
+ onClick: () => {
561
+ setScreenshot(null);
562
+ setScreenshotError("");
563
+ setCaptureStatus("");
564
+ },
565
+ children: _i18n._(
566
+ /** i18n */
567
+ {
568
+ id: "t_YqKh",
569
+ message: "Remove"
570
+ })
571
+ })]
572
+ })]
573
+ }), _jsxs("div", {
574
+ className: "mdbase-feedback-screenshot-actions",
575
+ children: [_jsx("button", {
576
+ type: "button",
577
+ disabled: imageBusy || submitting,
578
+ onClick: () => void capture(),
579
+ children: capturing ? _i18n._(
580
+ /** i18n */
581
+ {
582
+ id: "DJxqCW",
583
+ message: "Taking screenshot\u2026"
584
+ }) : screenshot ? _i18n._(
585
+ /** i18n */
586
+ {
587
+ id: "CRx2Ry",
588
+ message: "Retake screenshot"
589
+ }) : _i18n._(
590
+ /** i18n */
591
+ {
592
+ id: "Arxv23",
593
+ message: "Attach screenshot"
594
+ })
595
+ }), _jsxs("label", {
596
+ className: "mdbase-feedback-upload",
597
+ children: [_jsx("span", {
598
+ children: _i18n._(
599
+ /** i18n */
600
+ {
601
+ id: "BOOryO",
602
+ message: "Choose an image"
603
+ })
604
+ }), _jsx("input", {
605
+ type: "file",
606
+ accept: "image/png,image/jpeg",
607
+ disabled: imageBusy || submitting,
608
+ onChange: event => {
609
+ const file = event.target.files?.[0];
610
+ event.target.value = "";
611
+ if (file) void upload(file);
612
+ }
613
+ })]
614
+ })]
615
+ }), screenshot && _jsx("p", {
616
+ className: "mdbase-feedback-help",
617
+ children: _i18n._(
618
+ /** i18n */
619
+ {
620
+ id: "VBhfTn",
621
+ message: "Review for private information before sending."
622
+ })
623
+ }), captureStatus && _jsx("p", {
624
+ className: "mdbase-feedback-help",
625
+ role: "status",
626
+ children: captureStatus
627
+ }), screenshotError && _jsx("p", {
628
+ className: "mdbase-feedback-error",
629
+ role: "alert",
630
+ children: screenshotError
631
+ })]
632
+ }), _jsxs("details", {
633
+ className: "mdbase-feedback-details",
634
+ children: [_jsx("summary", {
635
+ children: _i18n._(
636
+ /** i18n */
637
+ {
638
+ id: "B5v8nx",
639
+ message: "What gets sent"
640
+ })
641
+ }), _jsx("p", {
642
+ className: "mdbase-feedback-help",
643
+ children: _i18n._(
644
+ /** i18n */
645
+ {
646
+ id: "HVAS1o",
647
+ message: "Your message, any reply email and included screenshot, plus these app and build details."
648
+ })
649
+ }), _jsx("pre", {
650
+ children: JSON.stringify(application, null, 2)
651
+ }), collectionName && _jsxs("label", {
652
+ className: "mdbase-feedback-check",
653
+ children: [_jsx("input", {
654
+ className: "mdbase-checkbox",
655
+ type: "checkbox",
656
+ checked: includeCollection,
657
+ disabled: submitting,
658
+ onChange: event => setIncludeCollection(event.target.checked)
659
+ }), _i18n._(
660
+ /** i18n */
661
+ {
662
+ id: "A--pCj",
663
+ message: "Include collection name:"
664
+ }), " ", collectionName]
665
+ }), topic === "problem" && _jsxs(_Fragment, {
666
+ children: [_jsxs("label", {
667
+ className: "mdbase-feedback-check",
668
+ children: [_jsx("input", {
669
+ className: "mdbase-checkbox",
670
+ type: "checkbox",
671
+ checked: includeDiagnostics,
672
+ disabled: submitting,
673
+ onChange: event => setIncludeDiagnostics(event.target.checked)
674
+ }), _i18n._(
675
+ /** i18n */
676
+ {
677
+ id: "7-61s4",
678
+ message: "Include technical diagnostics"
679
+ })]
680
+ }), _jsx("p", {
681
+ className: "mdbase-feedback-help",
682
+ children: _i18n._(
683
+ /** i18n */
684
+ {
685
+ id: "-ujXuR",
686
+ message: "Browser, OS, viewport and recent error codes. No note contents, paths or credentials."
687
+ })
688
+ }), includeDiagnostics && _jsx("pre", {
689
+ children: JSON.stringify(diagnostics, null, 2)
690
+ })]
691
+ }), _jsx("p", {
692
+ className: "mdbase-feedback-help",
693
+ children: _i18n._(
694
+ /** i18n */
695
+ {
696
+ id: "ZyjhMF",
697
+ message: "Images: PNG or JPEG, up to 3 MB. Blackout replaces pixels; only the final image is attached."
698
+ })
699
+ }), _jsx("p", {
700
+ className: "mdbase-feedback-help",
701
+ children: _i18n._(
702
+ /** i18n */
703
+ {
704
+ id: "i5ZjUk",
705
+ message: "Replies aren\u2019t guaranteed. Not for security reports or urgent support."
706
+ })
707
+ })]
708
+ }), turnstileSiteKey && open && !capturing && _jsx(FeedbackVerification, {
709
+ siteKey: turnstileSiteKey,
710
+ onToken: setTurnstileToken
711
+ }, verificationAttempt), submitError && _jsx("p", {
712
+ className: "mdbase-feedback-error",
713
+ role: "alert",
714
+ children: submitError
715
+ }), _jsx("p", {
716
+ className: "mdbase-feedback-help",
717
+ children: _i18n._(
718
+ /** i18n */
719
+ {
720
+ id: "UfEFtR",
721
+ message: "Private feedback to the mdbase team."
722
+ })
723
+ })]
724
+ }), _jsxs("footer", {
725
+ className: "mdbase-feedback-actions",
726
+ children: [_jsx("button", {
727
+ type: "button",
728
+ disabled: submitting,
729
+ onClick: close,
730
+ children: _i18n._(
731
+ /** i18n */
732
+ {
733
+ id: "dEgA5A",
734
+ message: "Cancel"
735
+ })
736
+ }), _jsx("button", {
737
+ type: "submit",
738
+ className: "mdbase-feedback-primary",
739
+ disabled: submitting || imageBusy || !message.trim() || Boolean(turnstileSiteKey && !turnstileToken),
740
+ children: submitting ? _i18n._(
741
+ /** i18n */
742
+ {
743
+ id: "Sad2tK",
744
+ message: "Sending\u2026"
745
+ }) : _i18n._(
746
+ /** i18n */
747
+ {
748
+ id: "RoafuO",
749
+ message: "Send feedback"
750
+ })
751
+ })]
752
+ })]
753
+ })]
754
+ }), markup && screenshot && _jsx(FeedbackMarkup, {
755
+ screenshot: screenshot,
756
+ onClose: () => {
757
+ setMarkup(false);
758
+ setCaptureStatus(_i18n._(
759
+ /** i18n */
760
+ {
761
+ id: "15fVx1",
762
+ message: "Markup discarded. Screenshot excluded."
763
+ }));
764
+ },
765
+ onApply: (next, count) => {
766
+ setScreenshot(next);
767
+ setIncludeScreenshot(true);
768
+ setMarkup(false);
769
+ setCaptureStatus(_i18n._(
770
+ /** i18n */
771
+ {
772
+ id: "ITLqil",
773
+ message: "{count, plural, one {# mark applied.} other {# marks applied.}}",
774
+ values: {
775
+ count: count
776
+ }
777
+ }));
778
+ }
779
+ })]
780
+ });
247
781
  }