feedbackkit-web 1.0.42 → 1.0.45
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/dist/feedbackkit.iife.js +20 -10
- package/dist/feedbackkit.iife.js.map +1 -1
- package/dist/feedbackkit.js +115 -38
- package/dist/feedbackkit.js.map +1 -1
- package/dist/types/submit.d.ts +2 -0
- package/dist/types/types.d.ts +7 -0
- package/dist/types/ui/styles.d.ts +1 -1
- package/package.json +1 -1
package/dist/feedbackkit.js
CHANGED
|
@@ -440,7 +440,8 @@ async function encodePayload(report, projectKey, identity = {}) {
|
|
|
440
440
|
created_at: report.createdAt.toISOString(),
|
|
441
441
|
text: report.text,
|
|
442
442
|
annotations: report.annotations,
|
|
443
|
-
environment: report.environment
|
|
443
|
+
environment: report.environment,
|
|
444
|
+
notify_reporter: report.notifyReporter ?? false
|
|
444
445
|
};
|
|
445
446
|
if (report.screenshotRaw && report.screenshotAnnotated) {
|
|
446
447
|
const [raw, annotated] = await Promise.all([
|
|
@@ -495,7 +496,7 @@ async function submitReport(report, configuration2, identity = {}) {
|
|
|
495
496
|
} catch {
|
|
496
497
|
}
|
|
497
498
|
const detail = code ? ` (${code})` : "";
|
|
498
|
-
const message = code === "origin_not_allowed" ? `This site (${location.origin}) isn't in the project's allowed origins.` : response.status === 401 || response.status === 403 ? `Server rejected the project key or origin (HTTP ${response.status})${detail}.` : response.status === 429 ? "Too many reports right now — please try again in a minute." : `Server returned HTTP ${response.status}${detail}.`;
|
|
499
|
+
const message = code === "origin_not_allowed" ? `This site (${location.origin}) isn't in the project's allowed origins.` : response.status === 401 || response.status === 403 ? `Server rejected the project key or origin (HTTP ${response.status})${detail}.` : response.status === 429 ? "Too many reports right now — please try again in a minute." : response.status === 402 ? "Feedback for this site is paused this month. Please try again later." : `Server returned HTTP ${response.status}${detail}.`;
|
|
499
500
|
throw new FeedbackSubmissionError(message, "server", response.status);
|
|
500
501
|
}
|
|
501
502
|
}
|
|
@@ -641,9 +642,7 @@ const icons = {
|
|
|
641
642
|
undo: svg('<path d="M9 14L4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/>'),
|
|
642
643
|
trash: svg('<path d="M4 7h16"/><path d="M10 11v6M14 11v6"/><path d="M6 7l1 13h10l1-13"/><path d="M9 7V4h6v3"/>'),
|
|
643
644
|
close: svg('<path d="M6 6l12 12M18 6L6 18"/>'),
|
|
644
|
-
|
|
645
|
-
'<path d="M21 11.5l-8.6 8.6a5.5 5.5 0 0 1-7.8-7.8l8.6-8.6a3.7 3.7 0 0 1 5.2 5.2l-8.6 8.6a1.8 1.8 0 0 1-2.6-2.6l7.9-7.9"/>'
|
|
646
|
-
),
|
|
645
|
+
plus: svg('<path d="M12 5v14M5 12h14"/>'),
|
|
647
646
|
send: svg('<path d="M4 12l16-8-6 16-3-7-7-1Z"/>'),
|
|
648
647
|
check: svg('<path d="M5 12.5l4.5 4.5L19 7.5"/>')
|
|
649
648
|
};
|
|
@@ -1527,6 +1526,16 @@ button:disabled { cursor: not-allowed; opacity: 0.45; }
|
|
|
1527
1526
|
color: var(--fk-secondary);
|
|
1528
1527
|
font-size: 13px;
|
|
1529
1528
|
}
|
|
1529
|
+
.fk-options-left { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
1530
|
+
.fk-menu-anchor { position: relative; display: inline-flex; }
|
|
1531
|
+
.fk-plus { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px dashed var(--fk-secondary); border-radius: 8px; background: transparent; color: var(--fk-secondary); cursor: pointer; }
|
|
1532
|
+
.fk-options-summary { font-size: 12px; color: var(--fk-muted); white-space: nowrap; }
|
|
1533
|
+
.fk-menu { position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 5; min-width: 230px; padding: 4px; border: 1px solid var(--fk-border); border-radius: 10px; background: var(--fk-bg); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); }
|
|
1534
|
+
.fk-menu-item { display: flex; align-items: center; gap: 6px; width: 100%; padding: 7px 10px 7px 6px; border: 0; border-radius: 6px; background: transparent; color: var(--fk-text); font-size: 13px; text-align: left; cursor: pointer; }
|
|
1535
|
+
.fk-menu-item:hover, .fk-menu-item:focus-visible { background: var(--fk-surface); outline: none; }
|
|
1536
|
+
.fk-menu-check { display: inline-flex; width: 16px; height: 16px; color: var(--fk-primary); }
|
|
1537
|
+
.fk-menu-check svg { width: 16px; height: 16px; }
|
|
1538
|
+
.fk-menu-sep { margin: 4px 0; border: 0; border-top: 1px solid var(--fk-border); }
|
|
1530
1539
|
.fk-file { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 4px 6px 4px 10px; border-radius: 8px; background: var(--fk-surface); font-size: 12px; }
|
|
1531
1540
|
.fk-file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1532
1541
|
.fk-file button { display: inline-flex; border: 0; background: transparent; color: var(--fk-muted); padding: 2px; }
|
|
@@ -1753,6 +1762,12 @@ class Dialog {
|
|
|
1753
1762
|
this.done = done;
|
|
1754
1763
|
this.editor = null;
|
|
1755
1764
|
this.includeLogs = true;
|
|
1765
|
+
this.notifyReporter = false;
|
|
1766
|
+
this.optionsMenu = null;
|
|
1767
|
+
this.onPointerDownOutsideMenu = (e) => {
|
|
1768
|
+
const anchor = this.optionsMenu?.parentElement;
|
|
1769
|
+
if (anchor && !e.composedPath().includes(anchor)) this.closeOptionsMenu();
|
|
1770
|
+
};
|
|
1756
1771
|
this.attachment = null;
|
|
1757
1772
|
this.selectedProducts = /* @__PURE__ */ new Set();
|
|
1758
1773
|
this.products = [];
|
|
@@ -1882,24 +1897,17 @@ class Dialog {
|
|
|
1882
1897
|
productsGroup.dataset.role = "products";
|
|
1883
1898
|
productsGroup.hidden = true;
|
|
1884
1899
|
const optionsRow = el("div", "fk-row");
|
|
1885
|
-
const toggle = switchControl("Screenshot", this.includeScreenshot, (on) => {
|
|
1886
|
-
this.includeScreenshot = on;
|
|
1887
|
-
this.stage.dataset.screenshot = on ? "on" : "off";
|
|
1888
|
-
this.refreshState();
|
|
1889
|
-
});
|
|
1890
|
-
if (!this.shot) toggle.querySelector("input").disabled = true;
|
|
1891
|
-
const attachGroup = el("div");
|
|
1892
1900
|
const fileInput = document.createElement("input");
|
|
1893
1901
|
fileInput.type = "file";
|
|
1894
1902
|
fileInput.hidden = true;
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const file =
|
|
1902
|
-
|
|
1903
|
+
const cameraInput = document.createElement("input");
|
|
1904
|
+
cameraInput.type = "file";
|
|
1905
|
+
cameraInput.accept = "image/*";
|
|
1906
|
+
cameraInput.setAttribute("capture", "environment");
|
|
1907
|
+
cameraInput.hidden = true;
|
|
1908
|
+
const onFile = (input) => {
|
|
1909
|
+
const file = input.files?.[0];
|
|
1910
|
+
input.value = "";
|
|
1903
1911
|
if (!file) return;
|
|
1904
1912
|
if (file.size > MAX_ATTACHMENT_BYTES) {
|
|
1905
1913
|
this.showError(`"${file.name}" is larger than 10 MB.`);
|
|
@@ -1907,26 +1915,84 @@ class Dialog {
|
|
|
1907
1915
|
}
|
|
1908
1916
|
this.attachment = { filename: file.name, mimeType: file.type || "application/octet-stream", data: file };
|
|
1909
1917
|
renderAttachment();
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1918
|
+
};
|
|
1919
|
+
fileInput.addEventListener("change", () => onFile(fileInput));
|
|
1920
|
+
cameraInput.addEventListener("change", () => onFile(cameraInput));
|
|
1921
|
+
const menuAnchor = el("div", "fk-menu-anchor");
|
|
1922
|
+
const menuButton = iconButton("fk-plus", icons.plus, "Options and attachments");
|
|
1923
|
+
menuButton.setAttribute("aria-haspopup", "menu");
|
|
1924
|
+
menuButton.setAttribute("aria-expanded", "false");
|
|
1925
|
+
menuButton.addEventListener("click", () => this.optionsMenu ? this.closeOptionsMenu() : openMenu());
|
|
1926
|
+
const summary = el("span", "fk-options-summary");
|
|
1927
|
+
const renderSummary = () => {
|
|
1928
|
+
const parts = [];
|
|
1929
|
+
if (this.shot && !this.includeScreenshot) parts.push("No screenshot");
|
|
1930
|
+
if (this.notifyReporter) parts.push("Notify me");
|
|
1931
|
+
summary.textContent = parts.join(" · ");
|
|
1932
|
+
};
|
|
1933
|
+
const openMenu = () => {
|
|
1934
|
+
const menu = el("div", "fk-menu");
|
|
1935
|
+
menu.setAttribute("role", "menu");
|
|
1936
|
+
const item = (label, onSelect, checked) => {
|
|
1937
|
+
const b = document.createElement("button");
|
|
1938
|
+
b.type = "button";
|
|
1939
|
+
b.className = "fk-menu-item";
|
|
1940
|
+
b.setAttribute("role", checked === void 0 ? "menuitem" : "menuitemcheckbox");
|
|
1941
|
+
if (checked !== void 0) b.setAttribute("aria-checked", String(checked));
|
|
1942
|
+
b.innerHTML = `<span class="fk-menu-check">${checked ? icons.check : ""}</span><span></span>`;
|
|
1943
|
+
b.lastElementChild.textContent = label;
|
|
1944
|
+
b.addEventListener("click", () => {
|
|
1945
|
+
this.closeOptionsMenu();
|
|
1946
|
+
onSelect();
|
|
1921
1947
|
});
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1948
|
+
return b;
|
|
1949
|
+
};
|
|
1950
|
+
menu.append(item("Attach file…", () => fileInput.click()));
|
|
1951
|
+
if (window.matchMedia?.("(pointer: coarse)").matches) menu.append(item("Take photo", () => cameraInput.click()));
|
|
1952
|
+
menu.append(el("hr", "fk-menu-sep"));
|
|
1953
|
+
if (this.shot) {
|
|
1954
|
+
menu.append(
|
|
1955
|
+
item("Include screenshot", () => {
|
|
1956
|
+
this.includeScreenshot = !this.includeScreenshot;
|
|
1957
|
+
this.stage.dataset.screenshot = this.includeScreenshot ? "on" : "off";
|
|
1958
|
+
renderSummary();
|
|
1959
|
+
this.refreshState();
|
|
1960
|
+
}, this.includeScreenshot)
|
|
1961
|
+
);
|
|
1926
1962
|
}
|
|
1963
|
+
menu.append(
|
|
1964
|
+
item("Notify me when it's fixed", () => {
|
|
1965
|
+
this.notifyReporter = !this.notifyReporter;
|
|
1966
|
+
renderSummary();
|
|
1967
|
+
}, this.notifyReporter)
|
|
1968
|
+
);
|
|
1969
|
+
menuAnchor.append(menu);
|
|
1970
|
+
menuButton.setAttribute("aria-expanded", "true");
|
|
1971
|
+
this.optionsMenu = menu;
|
|
1972
|
+
this.overlay.addEventListener("pointerdown", this.onPointerDownOutsideMenu, true);
|
|
1973
|
+
menu.querySelector(".fk-menu-item")?.focus();
|
|
1974
|
+
};
|
|
1975
|
+
const attachGroup = el("div", "fk-attach-group");
|
|
1976
|
+
const renderAttachment = () => {
|
|
1977
|
+
attachGroup.replaceChildren();
|
|
1978
|
+
if (!this.attachment) return;
|
|
1979
|
+
const chip = el("span", "fk-file");
|
|
1980
|
+
const name = el("span");
|
|
1981
|
+
name.textContent = this.attachment.filename;
|
|
1982
|
+
const remove = iconButton("", icons.close, `Remove ${this.attachment.filename}`);
|
|
1983
|
+
remove.addEventListener("click", () => {
|
|
1984
|
+
this.attachment = null;
|
|
1985
|
+
renderAttachment();
|
|
1986
|
+
});
|
|
1987
|
+
chip.append(name, remove);
|
|
1988
|
+
attachGroup.append(chip);
|
|
1927
1989
|
};
|
|
1928
1990
|
renderAttachment();
|
|
1929
|
-
|
|
1991
|
+
renderSummary();
|
|
1992
|
+
menuAnchor.append(menuButton, fileInput, cameraInput);
|
|
1993
|
+
const left = el("div", "fk-options-left");
|
|
1994
|
+
left.append(menuAnchor, attachGroup);
|
|
1995
|
+
optionsRow.append(left, summary);
|
|
1930
1996
|
const meta = this.buildDiagnostics();
|
|
1931
1997
|
this.errorBox = el("p", "fk-error");
|
|
1932
1998
|
this.errorBox.setAttribute("role", "alert");
|
|
@@ -2018,6 +2084,12 @@ class Dialog {
|
|
|
2018
2084
|
if (this.undoButton) this.undoButton.disabled = !this.editor?.canUndo;
|
|
2019
2085
|
if (this.clearButton) this.clearButton.disabled = !(this.editor && this.editor.annotations.length > 0);
|
|
2020
2086
|
}
|
|
2087
|
+
closeOptionsMenu() {
|
|
2088
|
+
this.optionsMenu?.remove();
|
|
2089
|
+
this.optionsMenu = null;
|
|
2090
|
+
this.overlay?.removeEventListener("pointerdown", this.onPointerDownOutsideMenu, true);
|
|
2091
|
+
this.overlay?.querySelector(".fk-plus")?.setAttribute("aria-expanded", "false");
|
|
2092
|
+
}
|
|
2021
2093
|
// ---------------------------------------------------------------- keyboard
|
|
2022
2094
|
handleKey(e) {
|
|
2023
2095
|
if (this.finished) return;
|
|
@@ -2025,7 +2097,11 @@ class Dialog {
|
|
|
2025
2097
|
const active = this.shadow.activeElement;
|
|
2026
2098
|
const typing = active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement;
|
|
2027
2099
|
if (active instanceof HTMLInputElement && active.classList.contains("fk-text-input")) return;
|
|
2028
|
-
if (e.key === "Escape") {
|
|
2100
|
+
if (e.key === "Escape" && this.optionsMenu) {
|
|
2101
|
+
e.preventDefault();
|
|
2102
|
+
e.stopPropagation();
|
|
2103
|
+
this.closeOptionsMenu();
|
|
2104
|
+
} else if (e.key === "Escape") {
|
|
2029
2105
|
e.preventDefault();
|
|
2030
2106
|
e.stopPropagation();
|
|
2031
2107
|
this.cancel();
|
|
@@ -2089,7 +2165,8 @@ class Dialog {
|
|
|
2089
2165
|
environment: this.environment,
|
|
2090
2166
|
attachment: this.attachment,
|
|
2091
2167
|
products: this.products.filter((p) => this.selectedProducts.has(p.key)),
|
|
2092
|
-
logs: this.includeLogs ? this.logs : []
|
|
2168
|
+
logs: this.includeLogs ? this.logs : [],
|
|
2169
|
+
notifyReporter: this.notifyReporter
|
|
2093
2170
|
};
|
|
2094
2171
|
}
|
|
2095
2172
|
async send() {
|