spaps-issue-reporting-react 0.4.2 → 0.6.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/CHANGELOG.md +12 -0
- package/README.md +90 -3
- package/dist/VoiceCapture-WX7J6BWQ.mjs +255 -0
- package/dist/index.d.mts +9 -138
- package/dist/index.d.ts +9 -138
- package/dist/index.js +970 -424
- package/dist/index.mjs +220 -224
- package/dist/screenshot-capture.d.mts +19 -0
- package/dist/screenshot-capture.d.ts +19 -0
- package/dist/screenshot-capture.js +315 -0
- package/dist/screenshot-capture.mjs +276 -0
- package/dist/types-D9U13O2X.d.mts +174 -0
- package/dist/types-D9U13O2X.d.ts +174 -0
- package/package.json +24 -5
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// src/components.tsx
|
|
2
2
|
import * as Dialog from "@radix-ui/react-dialog";
|
|
3
3
|
import * as Popover from "@radix-ui/react-popover";
|
|
4
|
-
import { useScribe } from "@elevenlabs/react";
|
|
5
4
|
import {
|
|
6
5
|
BugBeetle,
|
|
7
6
|
CheckCircle,
|
|
@@ -508,6 +507,7 @@ function IssueReportingProvider({
|
|
|
508
507
|
inputModes,
|
|
509
508
|
defaultInputMode,
|
|
510
509
|
voice,
|
|
510
|
+
screenshotCapture,
|
|
511
511
|
copy,
|
|
512
512
|
children
|
|
513
513
|
}) {
|
|
@@ -753,6 +753,7 @@ function IssueReportingProvider({
|
|
|
753
753
|
inputModes: resolvedInputModes,
|
|
754
754
|
defaultInputMode: resolvedDefaultInputMode,
|
|
755
755
|
voice: resolvedVoiceConfig,
|
|
756
|
+
screenshotCapture,
|
|
756
757
|
needsResponseIssueIds,
|
|
757
758
|
setNeedsResponse
|
|
758
759
|
}),
|
|
@@ -781,6 +782,7 @@ function IssueReportingProvider({
|
|
|
781
782
|
resolvedInputModes,
|
|
782
783
|
resolvedVoiceConfig,
|
|
783
784
|
scope,
|
|
785
|
+
screenshotCapture,
|
|
784
786
|
selectPanel,
|
|
785
787
|
setNeedsResponse,
|
|
786
788
|
startNewIssue
|
|
@@ -837,6 +839,9 @@ var POPOVER_SHADOW = "shadow-[0_18px_48px_rgba(15,23,42,0.18)]";
|
|
|
837
839
|
var MODAL_SHADOW = "shadow-[0_28px_80px_rgba(15,23,42,0.24)]";
|
|
838
840
|
var BADGE_TEXT = "text-[11px]";
|
|
839
841
|
var LABEL_TEXT = "text-[11px]";
|
|
842
|
+
var VoiceCapture = React2.lazy(() => import("./VoiceCapture-WX7J6BWQ.mjs"));
|
|
843
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect2 : React2.useLayoutEffect;
|
|
844
|
+
var SCREENSHOT_CAPTURE_METADATA_KEY = "screenshot_capture";
|
|
840
845
|
function truncate(value, max = 80) {
|
|
841
846
|
if (value.length <= max) {
|
|
842
847
|
return value;
|
|
@@ -857,8 +862,13 @@ function resolveErrorMessage(error, fallback) {
|
|
|
857
862
|
}
|
|
858
863
|
return fallback;
|
|
859
864
|
}
|
|
860
|
-
function
|
|
861
|
-
return
|
|
865
|
+
function buildScreenshotCaptureMetadata(config, status) {
|
|
866
|
+
return {
|
|
867
|
+
status,
|
|
868
|
+
scope: config.scope ?? "visible_viewport",
|
|
869
|
+
image_type: config.imageType ?? "image/png",
|
|
870
|
+
...status === "failed" ? { failure_reason: "capture_failed" } : {}
|
|
871
|
+
};
|
|
862
872
|
}
|
|
863
873
|
function appendTranscriptToNote(current, transcript) {
|
|
864
874
|
const normalizedCurrent = current.trim();
|
|
@@ -899,22 +909,14 @@ function getIssueOriginText(issue, copy) {
|
|
|
899
909
|
const humanName = issue.reporter_display_name?.trim();
|
|
900
910
|
return humanName ? `${copy.originHumanLabel} \xB7 ${humanName}` : copy.originHumanLabel;
|
|
901
911
|
}
|
|
902
|
-
function
|
|
912
|
+
function IssueReportVoiceActivationPanel({
|
|
903
913
|
canUseText,
|
|
904
914
|
effectiveInputMode,
|
|
905
915
|
isSubmitting,
|
|
906
|
-
isVoiceActive,
|
|
907
|
-
isVoiceConnecting,
|
|
908
|
-
voice,
|
|
909
|
-
voiceTokenResult,
|
|
910
|
-
committedTranscript,
|
|
911
|
-
voiceError,
|
|
912
|
-
scribeError,
|
|
913
916
|
onSelectText,
|
|
914
917
|
onSelectVoice,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
onAppendTranscript
|
|
918
|
+
onRequestStartVoiceInput,
|
|
919
|
+
onRequestAppendTranscript
|
|
918
920
|
}) {
|
|
919
921
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
920
922
|
canUseText ? /* @__PURE__ */ jsxs("div", { className: "mt-5 flex gap-2", children: [
|
|
@@ -951,53 +953,85 @@ function IssueReportVoicePanel({
|
|
|
951
953
|
}
|
|
952
954
|
)
|
|
953
955
|
] }) : null,
|
|
954
|
-
/* @__PURE__ */
|
|
955
|
-
/* @__PURE__ */ jsxs("div", {
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
|
|
958
|
-
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-slate-600", children: [
|
|
959
|
-
"Provider: ",
|
|
960
|
-
voiceTokenResult?.provider ?? voice.provider,
|
|
961
|
-
" \xB7 Model:",
|
|
962
|
-
" ",
|
|
963
|
-
voiceTokenResult?.model_id ?? voice.modelId
|
|
964
|
-
] }),
|
|
965
|
-
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-slate-500", children: voice.requireMicrophonePermission ? "Microphone access is required to transcribe." : "Microphone access policy is optional." })
|
|
966
|
-
] }),
|
|
967
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
968
|
-
isVoiceActive || isVoiceConnecting ? /* @__PURE__ */ jsx2(
|
|
969
|
-
"button",
|
|
970
|
-
{
|
|
971
|
-
type: "button",
|
|
972
|
-
className: "rounded-full border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-100",
|
|
973
|
-
onClick: onStopVoiceInput,
|
|
974
|
-
disabled: isSubmitting,
|
|
975
|
-
children: "Stop Voice Input"
|
|
976
|
-
}
|
|
977
|
-
) : /* @__PURE__ */ jsx2(
|
|
978
|
-
"button",
|
|
979
|
-
{
|
|
980
|
-
type: "button",
|
|
981
|
-
className: "rounded-full bg-slate-900 px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
982
|
-
onClick: onStartVoiceInput,
|
|
983
|
-
disabled: isSubmitting,
|
|
984
|
-
children: "Start Voice Input"
|
|
985
|
-
}
|
|
986
|
-
),
|
|
987
|
-
canUseText ? /* @__PURE__ */ jsx2(
|
|
988
|
-
"button",
|
|
989
|
-
{
|
|
990
|
-
type: "button",
|
|
991
|
-
className: "rounded-full border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-100 disabled:cursor-not-allowed disabled:opacity-60",
|
|
992
|
-
onClick: onAppendTranscript,
|
|
993
|
-
disabled: isSubmitting || !committedTranscript.trim(),
|
|
994
|
-
children: "Append Transcript"
|
|
995
|
-
}
|
|
996
|
-
) : null
|
|
997
|
-
] })
|
|
956
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-4 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
957
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
958
|
+
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium uppercase tracking-wide text-slate-500", children: "Voice Input" }),
|
|
959
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-slate-600", children: "Voice capture loads when selected." })
|
|
998
960
|
] }),
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
|
|
961
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
962
|
+
/* @__PURE__ */ jsx2(
|
|
963
|
+
"button",
|
|
964
|
+
{
|
|
965
|
+
type: "button",
|
|
966
|
+
className: "rounded-full bg-slate-900 px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
967
|
+
onClick: onRequestStartVoiceInput,
|
|
968
|
+
disabled: isSubmitting,
|
|
969
|
+
children: "Start Voice Input"
|
|
970
|
+
}
|
|
971
|
+
),
|
|
972
|
+
canUseText ? /* @__PURE__ */ jsx2(
|
|
973
|
+
"button",
|
|
974
|
+
{
|
|
975
|
+
type: "button",
|
|
976
|
+
className: "rounded-full border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-100 disabled:cursor-not-allowed disabled:opacity-60",
|
|
977
|
+
onClick: onRequestAppendTranscript,
|
|
978
|
+
disabled: isSubmitting,
|
|
979
|
+
children: "Append Transcript"
|
|
980
|
+
}
|
|
981
|
+
) : null
|
|
982
|
+
] })
|
|
983
|
+
] }) })
|
|
984
|
+
] });
|
|
985
|
+
}
|
|
986
|
+
function IssueReportVoiceFallback({
|
|
987
|
+
canUseText,
|
|
988
|
+
isSubmitting,
|
|
989
|
+
onSelectText,
|
|
990
|
+
onSelectVoice,
|
|
991
|
+
onRequestStartVoiceInput
|
|
992
|
+
}) {
|
|
993
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
994
|
+
canUseText ? /* @__PURE__ */ jsxs("div", { className: "mt-5 flex gap-2", children: [
|
|
995
|
+
/* @__PURE__ */ jsxs(
|
|
996
|
+
"button",
|
|
997
|
+
{
|
|
998
|
+
type: "button",
|
|
999
|
+
className: "inline-flex items-center gap-1 rounded-full border border-slate-200 px-3 py-1.5 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
1000
|
+
onClick: onSelectText,
|
|
1001
|
+
disabled: isSubmitting,
|
|
1002
|
+
children: [
|
|
1003
|
+
/* @__PURE__ */ jsx2(TextT, { className: "h-3.5 w-3.5" }),
|
|
1004
|
+
"Text Input"
|
|
1005
|
+
]
|
|
1006
|
+
}
|
|
1007
|
+
),
|
|
1008
|
+
/* @__PURE__ */ jsxs(
|
|
1009
|
+
"button",
|
|
1010
|
+
{
|
|
1011
|
+
type: "button",
|
|
1012
|
+
className: "inline-flex items-center gap-1 rounded-full border border-slate-900 bg-slate-900 px-3 py-1.5 text-xs font-semibold text-white transition",
|
|
1013
|
+
onClick: onSelectVoice,
|
|
1014
|
+
disabled: isSubmitting,
|
|
1015
|
+
children: [
|
|
1016
|
+
/* @__PURE__ */ jsx2(Microphone, { className: "h-3.5 w-3.5" }),
|
|
1017
|
+
"Voice Input"
|
|
1018
|
+
]
|
|
1019
|
+
}
|
|
1020
|
+
)
|
|
1021
|
+
] }) : null,
|
|
1022
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-600", children: [
|
|
1023
|
+
/* @__PURE__ */ jsx2(Spinner, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
1024
|
+
/* @__PURE__ */ jsx2("span", { className: "flex-1", children: "Loading voice input..." }),
|
|
1025
|
+
/* @__PURE__ */ jsx2(
|
|
1026
|
+
"button",
|
|
1027
|
+
{
|
|
1028
|
+
type: "button",
|
|
1029
|
+
className: "rounded-full bg-slate-900 px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
1030
|
+
onClick: onRequestStartVoiceInput,
|
|
1031
|
+
disabled: isSubmitting,
|
|
1032
|
+
children: "Start Voice Input"
|
|
1033
|
+
}
|
|
1034
|
+
)
|
|
1001
1035
|
] })
|
|
1002
1036
|
] });
|
|
1003
1037
|
}
|
|
@@ -1068,13 +1102,10 @@ function IssueReportModalBody({
|
|
|
1068
1102
|
normalizedNote,
|
|
1069
1103
|
isValid,
|
|
1070
1104
|
isSubmitting,
|
|
1071
|
-
isVoiceActive,
|
|
1072
|
-
isVoiceConnecting,
|
|
1073
1105
|
voice,
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
scribeError,
|
|
1106
|
+
createVoiceToken,
|
|
1107
|
+
voiceStartRequestId,
|
|
1108
|
+
voiceAppendRequestId,
|
|
1078
1109
|
submitError,
|
|
1079
1110
|
existingAttachments,
|
|
1080
1111
|
removedExistingIds,
|
|
@@ -1085,8 +1116,10 @@ function IssueReportModalBody({
|
|
|
1085
1116
|
onClose,
|
|
1086
1117
|
onSelectText,
|
|
1087
1118
|
onSelectVoice,
|
|
1088
|
-
|
|
1089
|
-
|
|
1119
|
+
onRequestStartVoiceInput,
|
|
1120
|
+
onRequestAppendTranscript,
|
|
1121
|
+
onTranscriptCommitted,
|
|
1122
|
+
onVoiceSubmitMetadataChange,
|
|
1090
1123
|
onAppendTranscript,
|
|
1091
1124
|
onNoteChange,
|
|
1092
1125
|
onSubmit,
|
|
@@ -1126,24 +1159,47 @@ function IssueReportModalBody({
|
|
|
1126
1159
|
] });
|
|
1127
1160
|
}
|
|
1128
1161
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1129
|
-
mode === "create" && canUseVoice ? /* @__PURE__ */ jsx2(
|
|
1130
|
-
|
|
1162
|
+
mode === "create" && canUseVoice ? effectiveInputMode === "voice" ? /* @__PURE__ */ jsx2(
|
|
1163
|
+
React2.Suspense,
|
|
1164
|
+
{
|
|
1165
|
+
fallback: /* @__PURE__ */ jsx2(
|
|
1166
|
+
IssueReportVoiceFallback,
|
|
1167
|
+
{
|
|
1168
|
+
canUseText,
|
|
1169
|
+
isSubmitting,
|
|
1170
|
+
onSelectText,
|
|
1171
|
+
onSelectVoice,
|
|
1172
|
+
onRequestStartVoiceInput
|
|
1173
|
+
}
|
|
1174
|
+
),
|
|
1175
|
+
children: /* @__PURE__ */ jsx2(
|
|
1176
|
+
VoiceCapture,
|
|
1177
|
+
{
|
|
1178
|
+
canUseText,
|
|
1179
|
+
effectiveInputMode,
|
|
1180
|
+
isSubmitting,
|
|
1181
|
+
voice,
|
|
1182
|
+
createVoiceToken,
|
|
1183
|
+
startRequestId: voiceStartRequestId,
|
|
1184
|
+
appendRequestId: voiceAppendRequestId,
|
|
1185
|
+
onSelectText,
|
|
1186
|
+
onSelectVoice,
|
|
1187
|
+
onTranscriptCommitted,
|
|
1188
|
+
onSubmitMetadataChange: onVoiceSubmitMetadataChange,
|
|
1189
|
+
onAppendTranscript
|
|
1190
|
+
}
|
|
1191
|
+
)
|
|
1192
|
+
}
|
|
1193
|
+
) : /* @__PURE__ */ jsx2(
|
|
1194
|
+
IssueReportVoiceActivationPanel,
|
|
1131
1195
|
{
|
|
1132
1196
|
canUseText,
|
|
1133
1197
|
effectiveInputMode,
|
|
1134
1198
|
isSubmitting,
|
|
1135
|
-
isVoiceActive,
|
|
1136
|
-
isVoiceConnecting,
|
|
1137
|
-
voice,
|
|
1138
|
-
voiceTokenResult,
|
|
1139
|
-
committedTranscript,
|
|
1140
|
-
voiceError,
|
|
1141
|
-
scribeError,
|
|
1142
1199
|
onSelectText,
|
|
1143
1200
|
onSelectVoice,
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
onAppendTranscript
|
|
1201
|
+
onRequestStartVoiceInput,
|
|
1202
|
+
onRequestAppendTranscript
|
|
1147
1203
|
}
|
|
1148
1204
|
) : null,
|
|
1149
1205
|
mode === "reply" && issue ? /* @__PURE__ */ jsxs("div", { className: "mt-5 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3", children: [
|
|
@@ -1205,101 +1261,6 @@ function IssueReportModalBody({
|
|
|
1205
1261
|
mode !== "create" && issue ? /* @__PURE__ */ jsx2(IssueReportMessageThread, { issueReportId: issue.id }) : null
|
|
1206
1262
|
] });
|
|
1207
1263
|
}
|
|
1208
|
-
function useIssueReportVoiceCapture({
|
|
1209
|
-
canUseVoice,
|
|
1210
|
-
defaultInputMode,
|
|
1211
|
-
isSubmitting,
|
|
1212
|
-
voice
|
|
1213
|
-
}) {
|
|
1214
|
-
const [inputMode, setInputMode] = useState2(defaultInputMode);
|
|
1215
|
-
const [voiceTokenResult, setVoiceTokenResult] = useState2(null);
|
|
1216
|
-
const [voiceSubmitMetadata, setVoiceSubmitMetadata] = useState2(null);
|
|
1217
|
-
const [voiceError, setVoiceError] = useState2(null);
|
|
1218
|
-
const {
|
|
1219
|
-
status: scribeStatus,
|
|
1220
|
-
isConnected,
|
|
1221
|
-
isTranscribing,
|
|
1222
|
-
committedTranscripts,
|
|
1223
|
-
error: scribeError,
|
|
1224
|
-
connect: connectScribe,
|
|
1225
|
-
disconnect: disconnectScribe
|
|
1226
|
-
} = useScribe({
|
|
1227
|
-
autoConnect: false,
|
|
1228
|
-
modelId: voice.modelId,
|
|
1229
|
-
microphone: voice.microphone
|
|
1230
|
-
});
|
|
1231
|
-
const committedTranscript = useMemo2(
|
|
1232
|
-
() => getCommittedTranscriptText(committedTranscripts),
|
|
1233
|
-
[committedTranscripts]
|
|
1234
|
-
);
|
|
1235
|
-
const isVoiceConnecting = scribeStatus === "connecting";
|
|
1236
|
-
const isVoiceActive = isConnected || isTranscribing;
|
|
1237
|
-
const resetVoiceCapture = useCallback2(() => {
|
|
1238
|
-
disconnectScribe();
|
|
1239
|
-
setInputMode(defaultInputMode);
|
|
1240
|
-
setVoiceTokenResult(null);
|
|
1241
|
-
setVoiceSubmitMetadata(null);
|
|
1242
|
-
setVoiceError(null);
|
|
1243
|
-
}, [defaultInputMode, disconnectScribe]);
|
|
1244
|
-
const startVoiceInput = useCallback2(
|
|
1245
|
-
async (createVoiceToken) => {
|
|
1246
|
-
if (!canUseVoice || isSubmitting || isVoiceActive || isVoiceConnecting) {
|
|
1247
|
-
return;
|
|
1248
|
-
}
|
|
1249
|
-
if (!createVoiceToken) {
|
|
1250
|
-
setVoiceError("Voice input is unavailable for this client.");
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
setVoiceError(null);
|
|
1254
|
-
try {
|
|
1255
|
-
const tokenResult = await createVoiceToken();
|
|
1256
|
-
setVoiceTokenResult(tokenResult);
|
|
1257
|
-
setVoiceSubmitMetadata(tokenResult);
|
|
1258
|
-
await connectScribe({
|
|
1259
|
-
token: tokenResult.token,
|
|
1260
|
-
modelId: tokenResult.model_id,
|
|
1261
|
-
microphone: voice.microphone
|
|
1262
|
-
});
|
|
1263
|
-
setInputMode("voice");
|
|
1264
|
-
} catch (startError) {
|
|
1265
|
-
setVoiceError(resolveErrorMessage(startError, "Failed to start voice input."));
|
|
1266
|
-
}
|
|
1267
|
-
},
|
|
1268
|
-
[
|
|
1269
|
-
canUseVoice,
|
|
1270
|
-
connectScribe,
|
|
1271
|
-
isSubmitting,
|
|
1272
|
-
isVoiceActive,
|
|
1273
|
-
isVoiceConnecting,
|
|
1274
|
-
voice.microphone
|
|
1275
|
-
]
|
|
1276
|
-
);
|
|
1277
|
-
const appendTranscript = useCallback2(
|
|
1278
|
-
(setNote) => {
|
|
1279
|
-
if (!committedTranscript.trim()) {
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
|
-
setNote((current) => appendTranscriptToNote(current, committedTranscript));
|
|
1283
|
-
setInputMode("text");
|
|
1284
|
-
},
|
|
1285
|
-
[committedTranscript]
|
|
1286
|
-
);
|
|
1287
|
-
return {
|
|
1288
|
-
inputMode,
|
|
1289
|
-
setInputMode,
|
|
1290
|
-
voiceTokenResult,
|
|
1291
|
-
voiceSubmitMetadata,
|
|
1292
|
-
committedTranscript,
|
|
1293
|
-
voiceError,
|
|
1294
|
-
scribeError,
|
|
1295
|
-
isVoiceConnecting,
|
|
1296
|
-
isVoiceActive,
|
|
1297
|
-
resetVoiceCapture,
|
|
1298
|
-
startVoiceInput,
|
|
1299
|
-
stopVoiceInput: disconnectScribe,
|
|
1300
|
-
appendTranscript
|
|
1301
|
-
};
|
|
1302
|
-
}
|
|
1303
1264
|
var INITIAL_UPLOAD_PROGRESS = {
|
|
1304
1265
|
phase: "idle",
|
|
1305
1266
|
uploaded: 0,
|
|
@@ -2079,11 +2040,18 @@ function IssueReportModal() {
|
|
|
2079
2040
|
retryModalHydration,
|
|
2080
2041
|
inputModes,
|
|
2081
2042
|
defaultInputMode,
|
|
2082
|
-
voice
|
|
2043
|
+
voice,
|
|
2044
|
+
screenshotCapture,
|
|
2045
|
+
createMode
|
|
2083
2046
|
} = useIssueReporting();
|
|
2084
2047
|
const { createMutation, updateMutation, replyMutation } = useIssueReportingMutations();
|
|
2085
2048
|
const [note, setNote] = useState2("");
|
|
2086
2049
|
const [submitError, setSubmitError] = useState2(null);
|
|
2050
|
+
const [inputMode, setInputMode] = useState2(defaultInputMode);
|
|
2051
|
+
const [committedTranscript, setCommittedTranscript] = useState2("");
|
|
2052
|
+
const [voiceSubmitMetadata, setVoiceSubmitMetadata] = useState2(null);
|
|
2053
|
+
const [voiceStartRequestId, setVoiceStartRequestId] = useState2(0);
|
|
2054
|
+
const [voiceAppendRequestId, setVoiceAppendRequestId] = useState2(0);
|
|
2087
2055
|
const { isOpen, mode, issue, target, isHydrating, error } = modalState;
|
|
2088
2056
|
const canUseVoice = mode === "create" && inputModes.includes("voice");
|
|
2089
2057
|
const canUseText = mode !== "create" || inputModes.includes("text");
|
|
@@ -2094,27 +2062,14 @@ function IssueReportModal() {
|
|
|
2094
2062
|
);
|
|
2095
2063
|
const attachmentState = useAttachmentState(existingAttachments);
|
|
2096
2064
|
const isSubmitting = createMutation.isPending || updateMutation.isPending || replyMutation.isPending || attachmentState.uploadProgress.phase === "uploading";
|
|
2097
|
-
const {
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
isVoiceConnecting,
|
|
2106
|
-
isVoiceActive,
|
|
2107
|
-
resetVoiceCapture,
|
|
2108
|
-
startVoiceInput,
|
|
2109
|
-
stopVoiceInput,
|
|
2110
|
-
appendTranscript
|
|
2111
|
-
} = useIssueReportVoiceCapture({
|
|
2112
|
-
canUseVoice,
|
|
2113
|
-
defaultInputMode,
|
|
2114
|
-
isSubmitting,
|
|
2115
|
-
voice
|
|
2116
|
-
});
|
|
2117
|
-
useEffect2(() => {
|
|
2065
|
+
const resetVoiceCapture = useCallback2(() => {
|
|
2066
|
+
setInputMode(defaultInputMode);
|
|
2067
|
+
setCommittedTranscript("");
|
|
2068
|
+
setVoiceSubmitMetadata(null);
|
|
2069
|
+
setVoiceStartRequestId(0);
|
|
2070
|
+
setVoiceAppendRequestId(0);
|
|
2071
|
+
}, [defaultInputMode]);
|
|
2072
|
+
useIsomorphicLayoutEffect(() => {
|
|
2118
2073
|
if (!isOpen) {
|
|
2119
2074
|
resetVoiceCapture();
|
|
2120
2075
|
attachmentState.reset();
|
|
@@ -2140,14 +2095,17 @@ function IssueReportModal() {
|
|
|
2140
2095
|
attachmentState.reset();
|
|
2141
2096
|
closeModal();
|
|
2142
2097
|
};
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2098
|
+
const handleRequestAppendTranscript = () => {
|
|
2099
|
+
setInputMode("voice");
|
|
2100
|
+
setVoiceAppendRequestId((current) => current + 1);
|
|
2145
2101
|
};
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2102
|
+
const handleRequestStartVoiceInput = () => {
|
|
2103
|
+
setInputMode("voice");
|
|
2104
|
+
setVoiceStartRequestId((current) => current + 1);
|
|
2148
2105
|
};
|
|
2149
|
-
const handleAppendTranscript = () => {
|
|
2150
|
-
|
|
2106
|
+
const handleAppendTranscript = (transcript) => {
|
|
2107
|
+
setNote((current) => appendTranscriptToNote(current, transcript));
|
|
2108
|
+
setInputMode("text");
|
|
2151
2109
|
};
|
|
2152
2110
|
const uploadPendingFiles = async () => {
|
|
2153
2111
|
const upload = client.issueReporting.uploadAttachment;
|
|
@@ -2189,8 +2147,41 @@ function IssueReportModal() {
|
|
|
2189
2147
|
try {
|
|
2190
2148
|
const noteForSubmit = normalizedNote;
|
|
2191
2149
|
let newAttachmentIds = [];
|
|
2150
|
+
let screenshotCaptureMetadata = null;
|
|
2151
|
+
if (screenshotCapture?.enabled && mode === "create" && canUseAttachments && client.issueReporting.uploadAttachment) {
|
|
2152
|
+
try {
|
|
2153
|
+
const { captureScreenshot } = await import("./screenshot-capture.mjs");
|
|
2154
|
+
const captureResult = await captureScreenshot(screenshotCapture, {
|
|
2155
|
+
pageUrl: target.page_url,
|
|
2156
|
+
createMode,
|
|
2157
|
+
inputMode: effectiveInputMode,
|
|
2158
|
+
target: {
|
|
2159
|
+
componentKey: target.component_key,
|
|
2160
|
+
componentLabel: target.component_label,
|
|
2161
|
+
surfaceRef: target.surface_ref,
|
|
2162
|
+
metadata: target.metadata
|
|
2163
|
+
}
|
|
2164
|
+
});
|
|
2165
|
+
const uploaded = await client.issueReporting.uploadAttachment(
|
|
2166
|
+
captureResult.blob,
|
|
2167
|
+
{ filename: captureResult.filename }
|
|
2168
|
+
);
|
|
2169
|
+
newAttachmentIds.push(uploaded.id);
|
|
2170
|
+
screenshotCaptureMetadata = buildScreenshotCaptureMetadata(
|
|
2171
|
+
screenshotCapture,
|
|
2172
|
+
"attached"
|
|
2173
|
+
);
|
|
2174
|
+
} catch (captureError) {
|
|
2175
|
+
screenshotCaptureMetadata = buildScreenshotCaptureMetadata(
|
|
2176
|
+
screenshotCapture,
|
|
2177
|
+
"failed"
|
|
2178
|
+
);
|
|
2179
|
+
screenshotCapture.onCaptureError?.(captureError);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2192
2182
|
if (canUseAttachments && attachmentState.pendingFiles.length > 0) {
|
|
2193
|
-
|
|
2183
|
+
const manualIds = await uploadPendingFiles();
|
|
2184
|
+
newAttachmentIds.push(...manualIds);
|
|
2194
2185
|
}
|
|
2195
2186
|
const effectiveVoiceMetadata = effectiveInputMode === "voice" ? voiceSubmitMetadata ?? {
|
|
2196
2187
|
provider: voice.provider,
|
|
@@ -2201,20 +2192,26 @@ function IssueReportModal() {
|
|
|
2201
2192
|
attach_transcript: true
|
|
2202
2193
|
} : null;
|
|
2203
2194
|
if (mode === "create") {
|
|
2195
|
+
const targetMetadata = {
|
|
2196
|
+
...target.metadata ?? {},
|
|
2197
|
+
...screenshotCaptureMetadata ? {
|
|
2198
|
+
[SCREENSHOT_CAPTURE_METADATA_KEY]: screenshotCaptureMetadata
|
|
2199
|
+
} : {},
|
|
2200
|
+
...effectiveVoiceMetadata && effectiveInputMode === "voice" ? {
|
|
2201
|
+
capture: {
|
|
2202
|
+
input_mode: "voice",
|
|
2203
|
+
provider: effectiveVoiceMetadata.provider,
|
|
2204
|
+
model_id: effectiveVoiceMetadata.model_id,
|
|
2205
|
+
retain_audio: effectiveVoiceMetadata.retain_audio,
|
|
2206
|
+
attach_transcript: effectiveVoiceMetadata.attach_transcript
|
|
2207
|
+
}
|
|
2208
|
+
} : {}
|
|
2209
|
+
};
|
|
2204
2210
|
await createMutation.mutateAsync({
|
|
2205
|
-
target:
|
|
2211
|
+
target: {
|
|
2206
2212
|
...target,
|
|
2207
|
-
metadata:
|
|
2208
|
-
|
|
2209
|
-
capture: {
|
|
2210
|
-
input_mode: "voice",
|
|
2211
|
-
provider: effectiveVoiceMetadata.provider,
|
|
2212
|
-
model_id: effectiveVoiceMetadata.model_id,
|
|
2213
|
-
retain_audio: effectiveVoiceMetadata.retain_audio,
|
|
2214
|
-
attach_transcript: effectiveVoiceMetadata.attach_transcript
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
} : target,
|
|
2213
|
+
metadata: targetMetadata
|
|
2214
|
+
},
|
|
2218
2215
|
note: noteForSubmit,
|
|
2219
2216
|
reporter_role_hint: reporterRoleHint,
|
|
2220
2217
|
attachment_ids: newAttachmentIds.length > 0 ? newAttachmentIds : void 0
|
|
@@ -2287,13 +2284,10 @@ function IssueReportModal() {
|
|
|
2287
2284
|
normalizedNote,
|
|
2288
2285
|
isValid,
|
|
2289
2286
|
isSubmitting,
|
|
2290
|
-
isVoiceActive,
|
|
2291
|
-
isVoiceConnecting,
|
|
2292
2287
|
voice,
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
scribeError,
|
|
2288
|
+
createVoiceToken: client.issueReporting.createVoiceToken,
|
|
2289
|
+
voiceStartRequestId,
|
|
2290
|
+
voiceAppendRequestId,
|
|
2297
2291
|
submitError,
|
|
2298
2292
|
existingAttachments,
|
|
2299
2293
|
removedExistingIds: attachmentState.removedExistingIds,
|
|
@@ -2304,8 +2298,10 @@ function IssueReportModal() {
|
|
|
2304
2298
|
onClose: handleCloseModal,
|
|
2305
2299
|
onSelectText: () => setInputMode("text"),
|
|
2306
2300
|
onSelectVoice: () => setInputMode("voice"),
|
|
2307
|
-
|
|
2308
|
-
|
|
2301
|
+
onRequestStartVoiceInput: handleRequestStartVoiceInput,
|
|
2302
|
+
onRequestAppendTranscript: handleRequestAppendTranscript,
|
|
2303
|
+
onTranscriptCommitted: setCommittedTranscript,
|
|
2304
|
+
onVoiceSubmitMetadataChange: setVoiceSubmitMetadata,
|
|
2309
2305
|
onAppendTranscript: handleAppendTranscript,
|
|
2310
2306
|
onNoteChange: setNote,
|
|
2311
2307
|
onSubmit: () => void handleSubmit(),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { j as IssueReportingScreenshotCaptureRect, f as IssueReportingScreenshotCaptureConfig, h as IssueReportingScreenshotCaptureContext } from './types-D9U13O2X.mjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
import 'spaps-types';
|
|
4
|
+
|
|
5
|
+
declare const SCREENSHOT_CAPTURE_REDACTION_TEXT = "[redacted]";
|
|
6
|
+
declare const ATTACHMENT_MAX_BYTES: number;
|
|
7
|
+
interface ScreenshotCaptureResult {
|
|
8
|
+
blob: Blob;
|
|
9
|
+
filename: string;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
byteSize: number;
|
|
12
|
+
rect: IssueReportingScreenshotCaptureRect;
|
|
13
|
+
scaled: boolean;
|
|
14
|
+
}
|
|
15
|
+
declare function captureScreenshot(config: IssueReportingScreenshotCaptureConfig, context: IssueReportingScreenshotCaptureContext): Promise<ScreenshotCaptureResult>;
|
|
16
|
+
declare function resetHtml2CanvasCache(): void;
|
|
17
|
+
declare function maskScreenshotCaptureDocument(clonedDocument: Document, excludeSelectors?: readonly string[], maskSelectors?: readonly string[]): void;
|
|
18
|
+
|
|
19
|
+
export { ATTACHMENT_MAX_BYTES, IssueReportingScreenshotCaptureConfig, IssueReportingScreenshotCaptureContext, IssueReportingScreenshotCaptureRect, SCREENSHOT_CAPTURE_REDACTION_TEXT, type ScreenshotCaptureResult, captureScreenshot, maskScreenshotCaptureDocument, resetHtml2CanvasCache };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { j as IssueReportingScreenshotCaptureRect, f as IssueReportingScreenshotCaptureConfig, h as IssueReportingScreenshotCaptureContext } from './types-D9U13O2X.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
import 'spaps-types';
|
|
4
|
+
|
|
5
|
+
declare const SCREENSHOT_CAPTURE_REDACTION_TEXT = "[redacted]";
|
|
6
|
+
declare const ATTACHMENT_MAX_BYTES: number;
|
|
7
|
+
interface ScreenshotCaptureResult {
|
|
8
|
+
blob: Blob;
|
|
9
|
+
filename: string;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
byteSize: number;
|
|
12
|
+
rect: IssueReportingScreenshotCaptureRect;
|
|
13
|
+
scaled: boolean;
|
|
14
|
+
}
|
|
15
|
+
declare function captureScreenshot(config: IssueReportingScreenshotCaptureConfig, context: IssueReportingScreenshotCaptureContext): Promise<ScreenshotCaptureResult>;
|
|
16
|
+
declare function resetHtml2CanvasCache(): void;
|
|
17
|
+
declare function maskScreenshotCaptureDocument(clonedDocument: Document, excludeSelectors?: readonly string[], maskSelectors?: readonly string[]): void;
|
|
18
|
+
|
|
19
|
+
export { ATTACHMENT_MAX_BYTES, IssueReportingScreenshotCaptureConfig, IssueReportingScreenshotCaptureContext, IssueReportingScreenshotCaptureRect, SCREENSHOT_CAPTURE_REDACTION_TEXT, type ScreenshotCaptureResult, captureScreenshot, maskScreenshotCaptureDocument, resetHtml2CanvasCache };
|