@goflowstate/widget-sdk 0.3.4 → 0.3.5
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 +5 -2
- package/dist/canvas-widget-sdk.js +174 -47
- package/dist/canvas-widget-sdk.js.map +2 -2
- package/dist/feedback.d.ts +5 -3
- package/dist/feedback.d.ts.map +1 -1
- package/dist/index.mjs +174 -47
- package/dist/index.mjs.map +2 -2
- package/package.json +1 -1
package/dist/feedback.d.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Flow feedback: the bug icon every flow carries (flowstate-docs plan 2026-11-09-flow-feedback).
|
|
3
3
|
*
|
|
4
4
|
* The session client mounts this when the session state says `feedbackEnabled`. A round button
|
|
5
|
-
* sits in the corner of the flow's own page. A tap
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* sits in the corner of the flow's own page. A tap opens a sheet (one field, one button) and
|
|
6
|
+
* takes a screenshot of the page as the person sees it. The sheet says each step: the picture is
|
|
7
|
+
* being taken, it is attached (or there is none, with a way to take it again), the note is
|
|
8
|
+
* sending, it is sent. The note goes out through the flow's own session
|
|
9
|
+
* (`session.request('feedback', …)`).
|
|
8
10
|
*
|
|
9
11
|
* It lives in the flow's document, in a shadow root, so it takes the flow's look instead of
|
|
10
12
|
* bringing one: the flow's fonts by inheritance, and its ground, ink, main button and corner
|
package/dist/feedback.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @goflowstate/widget-sdk v0.3.
|
|
1
|
+
// @goflowstate/widget-sdk v0.3.5
|
|
2
2
|
// https://github.com/goflowstate/goflowstate-sdk
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -43,7 +43,9 @@ function createServiceClient(opts) {
|
|
|
43
43
|
var FEEDBACK_HOST_ATTR = "data-fs-feedback-host";
|
|
44
44
|
var SCREENSHOT_MAX_BYTES = 15e4;
|
|
45
45
|
var SCREENSHOT_MAX_SIDE = 1280;
|
|
46
|
-
var CAPTURE_TIMEOUT_MS =
|
|
46
|
+
var CAPTURE_TIMEOUT_MS = 15e3;
|
|
47
|
+
var SEND_WAIT_MS = 4e3;
|
|
48
|
+
var SENT_HOLD_MS = 1600;
|
|
47
49
|
function parseColor(raw) {
|
|
48
50
|
const value = (raw ?? "").trim().toLowerCase();
|
|
49
51
|
if (!value || value === "transparent") return null;
|
|
@@ -473,11 +475,41 @@ h2 {
|
|
|
473
475
|
margin: 2px 0 0; font-family: var(--font-display); font-weight: var(--display-weight);
|
|
474
476
|
font-size: 22px; line-height: 1.15; letter-spacing: var(--display-tracking); text-wrap: balance;
|
|
475
477
|
}
|
|
476
|
-
.
|
|
477
|
-
.shot
|
|
478
|
-
.
|
|
479
|
-
|
|
480
|
-
|
|
478
|
+
.form { display: contents; }
|
|
479
|
+
.shot { display: flex; gap: 14px; align-items: center; font-size: 14px; }
|
|
480
|
+
.thumb {
|
|
481
|
+
position: relative; flex: none; width: 60px; aspect-ratio: var(--shot-ratio, 0.46); max-height: 124px;
|
|
482
|
+
border-radius: 9px; border: 1px solid var(--line); background: var(--fill);
|
|
483
|
+
}
|
|
484
|
+
.thumb img { display: none; width: 100%; height: 100%; object-fit: cover; object-position: top; border-radius: 8px; }
|
|
485
|
+
.scan, .empty, .badge { display: none; }
|
|
486
|
+
.shot[data-shot="taking"] .thumb { overflow: hidden; }
|
|
487
|
+
.shot[data-shot="taking"] .scan {
|
|
488
|
+
display: block; position: absolute; left: 0; right: 0; top: 0; height: 34%;
|
|
489
|
+
background: linear-gradient(to bottom, transparent, var(--line)); border-bottom: 2px solid var(--ink);
|
|
490
|
+
}
|
|
491
|
+
.shot[data-shot="attached"] .thumb { background: var(--surface); }
|
|
492
|
+
.shot[data-shot="attached"] img { display: block; }
|
|
493
|
+
.shot[data-shot="attached"] .badge {
|
|
494
|
+
display: grid; place-items: center; position: absolute; right: -8px; top: -8px; width: 22px; height: 22px;
|
|
495
|
+
border-radius: 50%; background: #1f7a4d; color: #fff; border: 2px solid var(--surface);
|
|
496
|
+
}
|
|
497
|
+
.badge svg { width: 12px; height: 12px; }
|
|
498
|
+
.shot[data-shot="none"] .thumb { border-style: dashed; background: none; }
|
|
499
|
+
.shot[data-shot="none"] .empty { display: grid; place-items: center; position: absolute; inset: 0; color: var(--ink-dim); }
|
|
500
|
+
.empty svg { width: 22px; height: 22px; }
|
|
501
|
+
.shot-text { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-width: 0; }
|
|
502
|
+
.shot-label { font-weight: 600; color: var(--ink); }
|
|
503
|
+
.shot[data-shot="none"] .shot-label { color: var(--ink-dim); font-weight: inherit; }
|
|
504
|
+
.finder {
|
|
505
|
+
position: fixed; z-index: 2147483002; pointer-events: none;
|
|
506
|
+
top: calc(10px + env(safe-area-inset-top, 0px)); left: 10px; right: 10px; bottom: var(--finder-bottom, 55%);
|
|
507
|
+
}
|
|
508
|
+
.finder i { position: absolute; width: 22px; height: 22px; border: 3px solid #fff; filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.5)); }
|
|
509
|
+
.finder i:nth-child(1) { left: 0; top: 0; border-right: 0; border-bottom: 0; border-top-left-radius: 9px; }
|
|
510
|
+
.finder i:nth-child(2) { right: 0; top: 0; border-left: 0; border-bottom: 0; border-top-right-radius: 9px; }
|
|
511
|
+
.finder i:nth-child(3) { left: 0; bottom: 0; border-right: 0; border-top: 0; border-bottom-left-radius: 9px; }
|
|
512
|
+
.finder i:nth-child(4) { right: 0; bottom: 0; border-left: 0; border-top: 0; border-bottom-right-radius: 9px; }
|
|
481
513
|
.link { background: none; border: 0; padding: 0; font: inherit; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
|
|
482
514
|
textarea {
|
|
483
515
|
width: 100%; min-height: 84px; resize: none; font: inherit; font-size: 16px; color: var(--ink);
|
|
@@ -490,7 +522,19 @@ textarea::placeholder { color: var(--ink-dim); }
|
|
|
490
522
|
cursor: pointer; border: 0; padding: 15px 14px;
|
|
491
523
|
background: var(--cta); color: var(--cta-ink); border-radius: var(--button-radius);
|
|
492
524
|
}
|
|
525
|
+
.send { display: flex; align-items: center; justify-content: center; gap: 9px; }
|
|
493
526
|
.send[disabled] { opacity: 0.5; cursor: default; }
|
|
527
|
+
.sheet[data-busy] .send { opacity: 1; }
|
|
528
|
+
.dots { display: inline-flex; gap: 4px; }
|
|
529
|
+
.dots i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: 0.4; }
|
|
530
|
+
.done { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 14px 0 10px; }
|
|
531
|
+
.ring { display: grid; place-items: center; width: 52px; height: 52px; border-radius: 50%; background: #1f7a4d; color: #fff; }
|
|
532
|
+
.ring svg { width: 26px; height: 26px; }
|
|
533
|
+
.done-title {
|
|
534
|
+
margin: 0; font-family: var(--font-display); font-weight: var(--display-weight);
|
|
535
|
+
font-size: 22px; line-height: 1.15; letter-spacing: var(--display-tracking);
|
|
536
|
+
}
|
|
537
|
+
.said { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
494
538
|
.status { margin: 0; font-size: 13px; color: var(--ink-dim); min-height: 1.4em; }
|
|
495
539
|
[hidden] { display: none !important; }
|
|
496
540
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -500,13 +544,31 @@ textarea::placeholder { color: var(--ink-dim); }
|
|
|
500
544
|
}
|
|
501
545
|
.bug svg { transition: transform 0.1s ease-out, width 0.22s ease, height 0.22s ease; }
|
|
502
546
|
.sheet { animation: rise 0.22s ease-out; }
|
|
503
|
-
.shot[data-
|
|
504
|
-
@keyframes
|
|
547
|
+
.shot[data-shot="taking"] .scan { animation: fsf-scan 1.3s cubic-bezier(0.45, 0, 0.55, 1) infinite; }
|
|
548
|
+
@keyframes fsf-scan { from { transform: translateY(-100%); } to { transform: translateY(300%); } }
|
|
549
|
+
.finder i { animation: fsf-frame 0.9s ease-in-out infinite alternate; }
|
|
550
|
+
@keyframes fsf-frame { from { opacity: 0.55; } to { opacity: 1; } }
|
|
551
|
+
.shot[data-shot="attached"] img { animation: fsf-land 0.28s cubic-bezier(0.2, 0.8, 0.2, 1); }
|
|
552
|
+
@keyframes fsf-land { from { transform: scale(1.14); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
553
|
+
.shot[data-shot="attached"] .badge { animation: fsf-pop 0.24s cubic-bezier(0.2, 0.8, 0.2, 1) 0.14s backwards; }
|
|
554
|
+
.ring { animation: fsf-pop 0.26s cubic-bezier(0.2, 0.8, 0.2, 1); }
|
|
555
|
+
@keyframes fsf-pop { from { transform: scale(0.4); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
556
|
+
.dots i { animation: fsf-dot 1s ease-in-out infinite; }
|
|
557
|
+
.dots i:nth-child(2) { animation-delay: 0.15s; }
|
|
558
|
+
.dots i:nth-child(3) { animation-delay: 0.3s; }
|
|
559
|
+
@keyframes fsf-dot { 30% { opacity: 1; transform: translateY(-2px); } 60% { opacity: 0.4; transform: none; } }
|
|
505
560
|
@keyframes rise { from { transform: translateY(24px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
506
561
|
}
|
|
507
562
|
`;
|
|
508
563
|
var mounted = null;
|
|
509
564
|
var feedbackOptedOut = (doc = document) => doc.documentElement.getAttribute("data-fs-feedback") === "off";
|
|
565
|
+
var CHECK_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
566
|
+
var CAMERA_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="6.5" width="17" height="12.5" rx="3"/><circle cx="12" cy="12.8" r="3.2"/><path d="M9 6.5l1-2h4l1 2"/></svg>';
|
|
567
|
+
var SHOT_LABEL = {
|
|
568
|
+
taking: "Taking a screenshot",
|
|
569
|
+
attached: "Screenshot attached",
|
|
570
|
+
none: "No screenshot"
|
|
571
|
+
};
|
|
510
572
|
var SCROLL_OPTIONS = { capture: true, passive: true };
|
|
511
573
|
function mountFlowFeedback(options) {
|
|
512
574
|
if (typeof document === "undefined" || !document.body) return null;
|
|
@@ -519,14 +581,22 @@ function mountFlowFeedback(options) {
|
|
|
519
581
|
<button type="button" class="bug" data-state="float" aria-label="Send feedback on this page">${BUG_ICON}</button>
|
|
520
582
|
<div class="probe" aria-hidden="true"></div>
|
|
521
583
|
<div class="scrim" hidden></div>
|
|
522
|
-
<div class="
|
|
584
|
+
<div class="finder" aria-hidden="true" hidden><i></i><i></i><i></i><i></i></div>
|
|
585
|
+
<div class="sheet" role="dialog" aria-modal="true" aria-labelledby="fsf-title" data-view="form" hidden>
|
|
523
586
|
<div class="grab" aria-hidden="true"></div>
|
|
524
|
-
<
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
587
|
+
<div class="form">
|
|
588
|
+
<h2 id="fsf-title">Feedback on this page</h2>
|
|
589
|
+
<div class="shot" data-shot="none">
|
|
590
|
+
<span class="thumb"><span class="scan" aria-hidden="true"></span><img alt="Screenshot of this page" /><span class="empty" aria-hidden="true">${CAMERA_ICON}</span><span class="badge" aria-hidden="true">${CHECK_ICON}</span></span>
|
|
591
|
+
<span class="shot-text"><span class="shot-label" role="status" aria-live="polite">No screenshot</span><button type="button" class="link remove" hidden>Remove</button><button type="button" class="link retake" hidden>Take screenshot</button></span>
|
|
592
|
+
</div>
|
|
593
|
+
<textarea aria-labelledby="fsf-title" placeholder="What should change?" maxlength="2000"></textarea>
|
|
594
|
+
<p class="status" role="status" aria-live="polite"></p>
|
|
595
|
+
<button type="button" class="send" disabled><span class="send-label">Send feedback</span><span class="dots" aria-hidden="true" hidden><i></i><i></i><i></i></span></button>
|
|
596
|
+
<button type="button" class="link tuck">Tuck the button to the side</button>
|
|
597
|
+
</div>
|
|
598
|
+
<div class="done" hidden><span class="ring">${CHECK_ICON}</span><p class="done-title" aria-hidden="true">Feedback sent</p></div>
|
|
599
|
+
<p class="said" role="status" aria-live="polite"></p>
|
|
530
600
|
</div>`;
|
|
531
601
|
const q = (selector) => root.querySelector(selector);
|
|
532
602
|
const bug = q(".bug");
|
|
@@ -536,6 +606,13 @@ function mountFlowFeedback(options) {
|
|
|
536
606
|
const img = q(".shot img");
|
|
537
607
|
const shotLabel = q(".shot-label");
|
|
538
608
|
const remove = q(".remove");
|
|
609
|
+
const retake = q(".retake");
|
|
610
|
+
const finder = q(".finder");
|
|
611
|
+
const form = q(".form");
|
|
612
|
+
const done = q(".done");
|
|
613
|
+
const said = q(".said");
|
|
614
|
+
const sendLabel = q(".send-label");
|
|
615
|
+
const dots = q(".dots");
|
|
539
616
|
const text = q("textarea");
|
|
540
617
|
const status = q(".status");
|
|
541
618
|
const send = q(".send");
|
|
@@ -761,14 +838,54 @@ function mountFlowFeedback(options) {
|
|
|
761
838
|
};
|
|
762
839
|
let round = 0;
|
|
763
840
|
let pending = null;
|
|
764
|
-
const setShot = (url) => {
|
|
765
|
-
screenshot = url;
|
|
766
|
-
shot.
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
841
|
+
const setShot = (state, url = null) => {
|
|
842
|
+
screenshot = state === "attached" ? url : null;
|
|
843
|
+
shot.dataset.shot = state;
|
|
844
|
+
shotLabel.textContent = SHOT_LABEL[state];
|
|
845
|
+
remove.hidden = state !== "attached" || busy;
|
|
846
|
+
retake.hidden = state !== "none" || busy;
|
|
847
|
+
finder.hidden = state !== "taking";
|
|
848
|
+
if (screenshot) img.src = screenshot;
|
|
770
849
|
else img.removeAttribute("src");
|
|
771
850
|
};
|
|
851
|
+
const setBusy = (next) => {
|
|
852
|
+
busy = next;
|
|
853
|
+
if (busy) sheet.setAttribute("data-busy", "");
|
|
854
|
+
else sheet.removeAttribute("data-busy");
|
|
855
|
+
text.readOnly = busy;
|
|
856
|
+
sendLabel.textContent = busy ? "Sending" : "Send feedback";
|
|
857
|
+
dots.hidden = !busy;
|
|
858
|
+
const state = shot.dataset.shot;
|
|
859
|
+
remove.hidden = state !== "attached" || busy;
|
|
860
|
+
retake.hidden = state !== "none" || busy;
|
|
861
|
+
sync();
|
|
862
|
+
};
|
|
863
|
+
const setView = (view) => {
|
|
864
|
+
sheet.dataset.view = view;
|
|
865
|
+
form.hidden = view !== "form";
|
|
866
|
+
done.hidden = view !== "sent";
|
|
867
|
+
said.textContent = view === "sent" ? "Feedback sent" : "";
|
|
868
|
+
};
|
|
869
|
+
const take = () => {
|
|
870
|
+
const mine = round += 1;
|
|
871
|
+
setShot("taking");
|
|
872
|
+
host.style.setProperty("--finder-bottom", `${sheet.offsetHeight + 10}px`);
|
|
873
|
+
host.style.setProperty(
|
|
874
|
+
"--shot-ratio",
|
|
875
|
+
String(Math.max(0.3, Math.min(1, window.innerWidth / Math.max(1, window.innerHeight))))
|
|
876
|
+
);
|
|
877
|
+
const capture = options.capture ?? (() => captureViewport(theme.ground));
|
|
878
|
+
const work = withTimeout(Promise.resolve().then(capture), CAPTURE_TIMEOUT_MS).then((url) => {
|
|
879
|
+
if (mine !== round) return;
|
|
880
|
+
pending = null;
|
|
881
|
+
setShot(url ? "attached" : "none", url);
|
|
882
|
+
const active = root.activeElement;
|
|
883
|
+
if (!busy && (!active || active === bug || active === retake || active === remove))
|
|
884
|
+
text.focus();
|
|
885
|
+
});
|
|
886
|
+
pending = work;
|
|
887
|
+
return work;
|
|
888
|
+
};
|
|
772
889
|
const close = () => {
|
|
773
890
|
if (closeTimer) clearTimeout(closeTimer);
|
|
774
891
|
closeTimer = null;
|
|
@@ -777,10 +894,11 @@ function mountFlowFeedback(options) {
|
|
|
777
894
|
sheet.hidden = true;
|
|
778
895
|
scrim.hidden = true;
|
|
779
896
|
bug.hidden = false;
|
|
780
|
-
|
|
897
|
+
setBusy(false);
|
|
898
|
+
setShot("none");
|
|
899
|
+
setView("form");
|
|
781
900
|
text.value = "";
|
|
782
901
|
status.textContent = "";
|
|
783
|
-
busy = false;
|
|
784
902
|
sync();
|
|
785
903
|
schedulePlace();
|
|
786
904
|
};
|
|
@@ -788,34 +906,34 @@ function mountFlowFeedback(options) {
|
|
|
788
906
|
if (!sheet.hidden) return pending ?? Promise.resolve();
|
|
789
907
|
if (busy) return Promise.resolve();
|
|
790
908
|
paint();
|
|
791
|
-
const mine = round += 1;
|
|
792
909
|
bug.hidden = true;
|
|
793
|
-
|
|
794
|
-
shot.hidden = false;
|
|
795
|
-
shot.setAttribute("data-pending", "");
|
|
796
|
-
shotLabel.textContent = "Taking a screenshot";
|
|
910
|
+
setView("form");
|
|
797
911
|
tuck.hidden = spot.state !== "float";
|
|
798
912
|
scrim.hidden = false;
|
|
799
913
|
sheet.hidden = false;
|
|
800
914
|
sync();
|
|
801
|
-
|
|
802
|
-
pending = withTimeout(Promise.resolve().then(capture), CAPTURE_TIMEOUT_MS).then((url) => {
|
|
803
|
-
if (mine !== round) return;
|
|
804
|
-
pending = null;
|
|
805
|
-
setShot(url);
|
|
806
|
-
if (!root.activeElement || root.activeElement === bug) text.focus();
|
|
807
|
-
});
|
|
808
|
-
return pending;
|
|
915
|
+
return take();
|
|
809
916
|
};
|
|
810
917
|
const submit = async () => {
|
|
811
918
|
const message = text.value.trim();
|
|
812
919
|
if (!message || busy) return;
|
|
813
|
-
|
|
814
|
-
sync();
|
|
920
|
+
setBusy(true);
|
|
815
921
|
status.textContent = "";
|
|
816
|
-
|
|
817
|
-
if (pending)
|
|
818
|
-
|
|
922
|
+
let mine = round;
|
|
923
|
+
if (pending) {
|
|
924
|
+
let timer;
|
|
925
|
+
await Promise.race([
|
|
926
|
+
pending,
|
|
927
|
+
new Promise((resolve) => timer = setTimeout(resolve, SEND_WAIT_MS))
|
|
928
|
+
]);
|
|
929
|
+
clearTimeout(timer);
|
|
930
|
+
if (mine !== round) return;
|
|
931
|
+
if (pending) {
|
|
932
|
+
mine = round += 1;
|
|
933
|
+
pending = null;
|
|
934
|
+
setShot("none");
|
|
935
|
+
}
|
|
936
|
+
}
|
|
819
937
|
try {
|
|
820
938
|
await options.submit({
|
|
821
939
|
message,
|
|
@@ -824,15 +942,16 @@ function mountFlowFeedback(options) {
|
|
|
824
942
|
pagePath: window.location.pathname.slice(-200),
|
|
825
943
|
viewport: { width: window.innerWidth, height: window.innerHeight }
|
|
826
944
|
});
|
|
827
|
-
|
|
945
|
+
if (mine !== round) return;
|
|
946
|
+
setView("sent");
|
|
828
947
|
closeTimer = setTimeout(() => {
|
|
829
948
|
close();
|
|
830
949
|
bug.focus();
|
|
831
|
-
},
|
|
950
|
+
}, SENT_HOLD_MS);
|
|
832
951
|
} catch {
|
|
833
|
-
|
|
952
|
+
if (mine !== round) return;
|
|
953
|
+
setBusy(false);
|
|
834
954
|
status.textContent = "That did not send. Try again.";
|
|
835
|
-
sync();
|
|
836
955
|
}
|
|
837
956
|
};
|
|
838
957
|
bug.addEventListener("click", () => {
|
|
@@ -866,7 +985,15 @@ function mountFlowFeedback(options) {
|
|
|
866
985
|
remove.addEventListener("click", () => {
|
|
867
986
|
round += 1;
|
|
868
987
|
pending = null;
|
|
869
|
-
setShot(
|
|
988
|
+
setShot("none");
|
|
989
|
+
retake.focus();
|
|
990
|
+
});
|
|
991
|
+
retake.addEventListener("click", () => void take());
|
|
992
|
+
sheet.addEventListener("click", () => {
|
|
993
|
+
if (sheet.dataset.view === "sent") {
|
|
994
|
+
close();
|
|
995
|
+
bug.focus();
|
|
996
|
+
}
|
|
870
997
|
});
|
|
871
998
|
text.addEventListener("input", sync);
|
|
872
999
|
send.addEventListener("click", () => void submit());
|