@sphoro/bugcatch-web-sdk 0.17.0 → 0.19.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 +104 -6
- package/dist/bugcatch-call.es.js +587 -0
- package/dist/bugcatch-call.es.js.map +1 -0
- package/dist/bugcatch-call.umd.js +2 -0
- package/dist/bugcatch-call.umd.js.map +1 -0
- package/dist/bugcatch-core.es.js +3292 -0
- package/dist/bugcatch-core.es.js.map +1 -0
- package/dist/bugcatch-panel.es.js +2531 -0
- package/dist/bugcatch-panel.es.js.map +1 -0
- package/dist/bugcatch-panel.umd.js +1311 -0
- package/dist/bugcatch-panel.umd.js.map +1 -0
- package/dist/bugcatch.es.js +2 -5625
- package/dist/bugcatch.es.js.map +1 -1
- package/dist/bugcatch.standalone.umd.js +1619 -0
- package/dist/bugcatch.standalone.umd.js.map +1 -0
- package/dist/bugcatch.umd.js +195 -1318
- package/dist/bugcatch.umd.js.map +1 -1
- package/package.json +6 -6
- package/src/accent.js +38 -0
- package/src/announcements.js +147 -0
- package/src/busy.js +18 -0
- package/src/call.js +7 -0
- package/src/capture.js +9 -0
- package/src/chunks.js +56 -0
- package/src/gesture.js +33 -0
- package/src/icons.js +18 -0
- package/src/identity.js +13 -8
- package/src/incoming-call.js +25 -45
- package/src/index.d.ts +93 -2
- package/src/index.js +164 -42
- package/src/lazy.js +23 -0
- package/src/notice.js +68 -0
- package/src/panel/call.js +242 -0
- package/src/panel/compose.js +416 -0
- package/src/panel/composer.js +235 -0
- package/src/panel/icons.js +36 -0
- package/src/panel/index.js +265 -0
- package/src/panel/known.js +130 -0
- package/src/panel/messages.js +176 -0
- package/src/panel/notices.js +34 -0
- package/src/panel/picks.js +65 -0
- package/src/panel/refs.js +321 -0
- package/src/panel/render.js +132 -0
- package/src/panel/signin.js +228 -0
- package/src/panel/styles.js +1016 -0
- package/src/panel/thread.js +494 -0
- package/src/session.js +8 -3
- package/src/umd/call.js +6 -0
- package/src/umd/lazy.js +98 -0
- package/src/umd/panel.js +6 -0
- package/src/updates.js +97 -34
- package/src/widget.js +385 -3376
package/src/updates.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
// let anyone read anyone's reports. Holding this token is proof of having
|
|
12
12
|
// filed them.
|
|
13
13
|
|
|
14
|
+
import { tooManyMessage } from './busy.js';
|
|
15
|
+
import { readableOn, INK, PAPER } from './accent.js';
|
|
16
|
+
|
|
14
17
|
const TOKEN_KEY = 'bugcatch.watch';
|
|
15
18
|
const BACKOFF_MS = [2000, 5000, 15000, 30000, 60000];
|
|
16
19
|
|
|
@@ -308,6 +311,7 @@ export async function sendMessage({ apiBase, bugId, body, attachments }) {
|
|
|
308
311
|
const problem = await res.json();
|
|
309
312
|
detail = Array.isArray(problem.message) ? problem.message.join(', ') : problem.message || '';
|
|
310
313
|
} catch { /* non-JSON error */ }
|
|
314
|
+
if (res.status === 429) throw new Error(tooManyMessage(detail));
|
|
311
315
|
throw new Error(detail || `[BugCatch] could not send that (HTTP ${res.status}).`);
|
|
312
316
|
}
|
|
313
317
|
return res.json();
|
|
@@ -384,7 +388,7 @@ export async function callKindsForProject({ apiBase, apiKey, projectId }) {
|
|
|
384
388
|
* call could be placed — which is a success for the report and a failure only
|
|
385
389
|
* for the call, and the caller is expected to tell them apart.
|
|
386
390
|
*/
|
|
387
|
-
export async function reportByCall({ apiBase, apiKey, token, payload }) {
|
|
391
|
+
export async function reportByCall({ apiBase, apiKey, token, payload, watchToken }) {
|
|
388
392
|
const res = await fetch(`${apiBase}/ingest/calls`, {
|
|
389
393
|
method: 'POST',
|
|
390
394
|
headers: {
|
|
@@ -396,7 +400,9 @@ export async function reportByCall({ apiBase, apiKey, token, payload }) {
|
|
|
396
400
|
// is saying, an inch below the message, who they are signed in as.
|
|
397
401
|
...(token ? { 'X-BugCatch-Session': token } : {}),
|
|
398
402
|
},
|
|
399
|
-
|
|
403
|
+
// The watch token this browser holds, so the one filing hands back covers
|
|
404
|
+
// the reports it already could — see withHeldToken in index.js.
|
|
405
|
+
body: JSON.stringify(watchToken ? { ...payload, watchToken } : payload),
|
|
400
406
|
});
|
|
401
407
|
|
|
402
408
|
if (!res.ok) {
|
|
@@ -406,7 +412,8 @@ export async function reportByCall({ apiBase, apiKey, token, payload }) {
|
|
|
406
412
|
detail = Array.isArray(problem.message) ? problem.message.join(', ') : problem.message || '';
|
|
407
413
|
} catch { /* non-JSON error */ }
|
|
408
414
|
const err = new Error(
|
|
409
|
-
|
|
415
|
+
res.status === 429 ? tooManyMessage(detail)
|
|
416
|
+
: detail || `[BugCatch] the call could not be started (HTTP ${res.status}).`,
|
|
410
417
|
);
|
|
411
418
|
// Carried so a session the server has stopped recognising can be told from
|
|
412
419
|
// every other refusal and forgotten, rather than pressing Call into the
|
|
@@ -652,9 +659,21 @@ const STATUS_COPY = {
|
|
|
652
659
|
*/
|
|
653
660
|
const KIND_ACCENT = {
|
|
654
661
|
status: null, // falls back to the host's configured accent
|
|
655
|
-
severity: '#
|
|
656
|
-
assignment: '#
|
|
657
|
-
reply: '#
|
|
662
|
+
severity: '#f2a54a',
|
|
663
|
+
assignment: '#79a8ff',
|
|
664
|
+
reply: '#4fd19a',
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// The cards are always dark — they float over pages of every colour, and a
|
|
668
|
+
// near-black card with a hairline reads on all of them. The colours are the
|
|
669
|
+
// product's dark palette.
|
|
670
|
+
const CARD = {
|
|
671
|
+
bg: '#111113',
|
|
672
|
+
fg: '#f3f2ef',
|
|
673
|
+
muted: '#b3b0aa',
|
|
674
|
+
faint: '#8f8c86',
|
|
675
|
+
ring: 'rgba(255,255,255,.09)',
|
|
676
|
+
brand: '#a59cff',
|
|
658
677
|
};
|
|
659
678
|
|
|
660
679
|
/**
|
|
@@ -730,7 +749,7 @@ function dockOffset() {
|
|
|
730
749
|
* instead of a notification floating over the report it is about.
|
|
731
750
|
*/
|
|
732
751
|
export function createUpdateNotifier({
|
|
733
|
-
accent =
|
|
752
|
+
accent = null,
|
|
734
753
|
position = 'bottom-right',
|
|
735
754
|
onRead = null,
|
|
736
755
|
onReadAll = null,
|
|
@@ -778,17 +797,23 @@ export function createUpdateNotifier({
|
|
|
778
797
|
setTimeout(() => el.remove(), 250);
|
|
779
798
|
}
|
|
780
799
|
|
|
800
|
+
// On a dark card the ink default is a white pill with ink words; a host's
|
|
801
|
+
// own colour keeps whichever words read on it.
|
|
802
|
+
const primaryBg = accent || PAPER;
|
|
803
|
+
const primaryFg = accent ? readableOn(accent) : INK;
|
|
804
|
+
|
|
781
805
|
function button(label, primary) {
|
|
782
806
|
const el = document.createElement('button');
|
|
783
807
|
el.type = 'button';
|
|
784
808
|
el.textContent = label;
|
|
785
809
|
el.style.cssText = [
|
|
786
|
-
'padding:
|
|
787
|
-
|
|
788
|
-
'
|
|
789
|
-
|
|
790
|
-
`
|
|
791
|
-
|
|
810
|
+
'padding:6px 12px',
|
|
811
|
+
'border:0',
|
|
812
|
+
`box-shadow:${primary ? 'none' : `inset 0 0 0 1px ${CARD.ring}`}`,
|
|
813
|
+
'border-radius:999px',
|
|
814
|
+
`background:${primary ? primaryBg : 'rgba(255,255,255,.06)'}`,
|
|
815
|
+
`color:${primary ? primaryFg : CARD.fg}`,
|
|
816
|
+
'font:560 12px/1.4 inherit',
|
|
792
817
|
'cursor:pointer',
|
|
793
818
|
'white-space:nowrap',
|
|
794
819
|
].join(';');
|
|
@@ -801,13 +826,12 @@ export function createUpdateNotifier({
|
|
|
801
826
|
el.style.cssText = [
|
|
802
827
|
'box-sizing:border-box',
|
|
803
828
|
'width:100%',
|
|
804
|
-
'padding:
|
|
805
|
-
'border-radius:
|
|
806
|
-
|
|
807
|
-
|
|
829
|
+
'padding:13px 14px',
|
|
830
|
+
'border-radius:14px',
|
|
831
|
+
`background:${CARD.bg}`,
|
|
832
|
+
`color:${CARD.fg}`,
|
|
808
833
|
'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
|
|
809
|
-
|
|
810
|
-
`border-left:3px solid ${KIND_ACCENT[record?.kind] || accent}`,
|
|
834
|
+
`box-shadow:0 0 0 1px ${CARD.ring},0 20px 44px -12px rgba(0,0,0,.55),0 4px 12px rgba(0,0,0,.25)`,
|
|
811
835
|
'opacity:0',
|
|
812
836
|
`transform:translateY(${atTop ? -8 : 8}px)`,
|
|
813
837
|
'transition:opacity .2s ease,transform .2s ease',
|
|
@@ -815,25 +839,33 @@ export function createUpdateNotifier({
|
|
|
815
839
|
].join(';');
|
|
816
840
|
|
|
817
841
|
const title = document.createElement('div');
|
|
818
|
-
title.style.cssText = 'font-weight:600;margin-bottom:2px';
|
|
819
|
-
|
|
842
|
+
title.style.cssText = 'display:flex;align-items:baseline;gap:8px;font-weight:600;margin-bottom:2px';
|
|
843
|
+
// The kind's colour, as a dot before the headline rather than a stripe
|
|
844
|
+
// down the card: a fix and a severity change are still told apart.
|
|
845
|
+
const dot = document.createElement('span');
|
|
846
|
+
dot.setAttribute('aria-hidden', 'true');
|
|
847
|
+
dot.style.cssText = `flex:none;width:7px;height:7px;border-radius:50%;transform:translateY(-1px);background:${
|
|
848
|
+
KIND_ACCENT[record?.kind] || accent || CARD.brand}`;
|
|
849
|
+
const headline = document.createElement('span');
|
|
850
|
+
headline.textContent = updateHeadline(record);
|
|
851
|
+
title.append(dot, headline);
|
|
820
852
|
|
|
821
853
|
const body = document.createElement('div');
|
|
822
854
|
// A reply is the one kind whose detail is worth more room than a title, and
|
|
823
855
|
// it is the only text here the team wrote rather than the reporter.
|
|
824
856
|
body.style.cssText = record?.kind === 'reply'
|
|
825
|
-
?
|
|
826
|
-
:
|
|
857
|
+
? `color:${CARD.muted};padding-left:15px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden`
|
|
858
|
+
: `color:${CARD.muted};padding-left:15px`;
|
|
827
859
|
// textContent, not innerHTML: this is text a person typed, being written
|
|
828
860
|
// back into somebody else's page.
|
|
829
861
|
body.textContent = updateDetail(record);
|
|
830
862
|
|
|
831
863
|
const actions = document.createElement('div');
|
|
832
864
|
actions.style.cssText =
|
|
833
|
-
'display:flex;align-items:center;gap:8px;margin-top:10px;flex-wrap:wrap';
|
|
865
|
+
'display:flex;align-items:center;gap:8px;margin-top:10px;padding-left:15px;flex-wrap:wrap';
|
|
834
866
|
|
|
835
867
|
const when = document.createElement('span');
|
|
836
|
-
when.style.cssText =
|
|
868
|
+
when.style.cssText = `color:${CARD.faint};font-size:11px;margin-right:auto`;
|
|
837
869
|
when.textContent = agoLabel(record?.at);
|
|
838
870
|
|
|
839
871
|
const read = button('Mark as read', false);
|
|
@@ -865,12 +897,12 @@ export function createUpdateNotifier({
|
|
|
865
897
|
'display:flex',
|
|
866
898
|
'align-items:center',
|
|
867
899
|
'gap:8px',
|
|
868
|
-
'padding:8px
|
|
869
|
-
'border-radius:
|
|
870
|
-
|
|
871
|
-
|
|
900
|
+
'padding:8px 10px 8px 14px',
|
|
901
|
+
'border-radius:999px',
|
|
902
|
+
`background:${CARD.bg}`,
|
|
903
|
+
`color:${CARD.muted}`,
|
|
872
904
|
'font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',
|
|
873
|
-
|
|
905
|
+
`box-shadow:0 0 0 1px ${CARD.ring},0 12px 30px -10px rgba(0,0,0,.5)`,
|
|
874
906
|
'pointer-events:auto',
|
|
875
907
|
].join(';');
|
|
876
908
|
|
|
@@ -1020,12 +1052,18 @@ export async function matchKnownIssues({ apiBase, apiKey, projectId, q, sessionT
|
|
|
1020
1052
|
export async function joinKnownIssue({
|
|
1021
1053
|
apiBase, apiKey, projectId, bugId, reporterEmail, reporterName, metadata, logs, sessionToken,
|
|
1022
1054
|
}) {
|
|
1055
|
+
// The token this browser already holds goes back with the join, so the one
|
|
1056
|
+
// that comes back still covers the reports it filed as well as this issue.
|
|
1057
|
+
const held = readToken();
|
|
1023
1058
|
const res = await fetch(
|
|
1024
1059
|
`${apiBase}/ingest/known-issues/${encodeURIComponent(bugId)}/join`,
|
|
1025
1060
|
{
|
|
1026
1061
|
method: 'POST',
|
|
1027
1062
|
headers: knownIssueHeaders(apiKey, sessionToken),
|
|
1028
|
-
body: JSON.stringify({
|
|
1063
|
+
body: JSON.stringify({
|
|
1064
|
+
projectId, reporterEmail, reporterName, metadata, logs,
|
|
1065
|
+
...(held ? { watchToken: held } : {}),
|
|
1066
|
+
}),
|
|
1029
1067
|
},
|
|
1030
1068
|
);
|
|
1031
1069
|
if (!res.ok) throw new Error(await readJoinError(res));
|
|
@@ -1043,12 +1081,37 @@ function knownIssueHeaders(apiKey, sessionToken) {
|
|
|
1043
1081
|
}
|
|
1044
1082
|
|
|
1045
1083
|
async function readJoinError(res) {
|
|
1084
|
+
let message = '';
|
|
1046
1085
|
try {
|
|
1047
1086
|
const body = await res.json();
|
|
1048
|
-
|
|
1049
|
-
if (message) return message;
|
|
1087
|
+
message = Array.isArray(body.message) ? body.message.join(', ') : body.message || '';
|
|
1050
1088
|
} catch { /* non-JSON error */ }
|
|
1051
|
-
|
|
1089
|
+
if (res.status === 429) return tooManyMessage(message);
|
|
1090
|
+
return message || `Could not add you to this issue (HTTP ${res.status})`;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Rate how a settled report was handled, out of five, with a sentence if they
|
|
1095
|
+
* want one. Rating again replaces the earlier score.
|
|
1096
|
+
*
|
|
1097
|
+
* Same credential as everything else here: the watch token, which is proof of
|
|
1098
|
+
* having filed the report. `?projectId=` goes with it so a token that reaches
|
|
1099
|
+
* more than one project is narrowed to the one this widget is on.
|
|
1100
|
+
*/
|
|
1101
|
+
export async function rateReport({ apiBase, bugId, score, comment }) {
|
|
1102
|
+
const token = readToken();
|
|
1103
|
+
if (!token) throw new Error('This browser cannot access that report.');
|
|
1104
|
+
const res = await fetch(`${apiBase}/ingest/watch/reports/${encodeURIComponent(bugId)}/rating${projectQuery()}`, {
|
|
1105
|
+
method: 'POST',
|
|
1106
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
1107
|
+
body: JSON.stringify({ score, ...(comment ? { comment } : {}) }),
|
|
1108
|
+
});
|
|
1109
|
+
const data = await res.json().catch(() => ({}));
|
|
1110
|
+
if (!res.ok) {
|
|
1111
|
+
const message = Array.isArray(data.message) ? data.message.join(', ') : data.message;
|
|
1112
|
+
throw new Error(message || 'Could not send your rating. Please try again.');
|
|
1113
|
+
}
|
|
1114
|
+
return data;
|
|
1052
1115
|
}
|
|
1053
1116
|
|
|
1054
1117
|
export async function verifyFix({ apiBase, bugId, ...feedback }) {
|