spaps-issue-reporting-react 0.1.2 → 0.1.4
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 +8 -0
- package/README.md +87 -7
- package/dist/index.d.mts +44 -4
- package/dist/index.d.ts +44 -4
- package/dist/index.js +902 -147
- package/dist/index.mjs +895 -138
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
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";
|
|
4
5
|
import {
|
|
5
6
|
BugBeetle,
|
|
6
7
|
CheckCircle,
|
|
7
8
|
Circle,
|
|
9
|
+
Microphone,
|
|
8
10
|
Spinner,
|
|
11
|
+
TextT,
|
|
9
12
|
X
|
|
10
13
|
} from "@phosphor-icons/react";
|
|
11
14
|
import { formatDistanceToNow } from "date-fns";
|
|
12
|
-
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
15
|
+
import React2, { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
13
16
|
|
|
14
17
|
// src/provider.tsx
|
|
15
18
|
import {
|
|
@@ -18,6 +21,7 @@ import {
|
|
|
18
21
|
useContext as useContext2,
|
|
19
22
|
useEffect,
|
|
20
23
|
useMemo,
|
|
24
|
+
useRef,
|
|
21
25
|
useState
|
|
22
26
|
} from "react";
|
|
23
27
|
import {
|
|
@@ -40,6 +44,9 @@ import { jsx } from "react/jsx-runtime";
|
|
|
40
44
|
var LIST_LIMIT = 200;
|
|
41
45
|
var NOTE_MIN_LENGTH = 10;
|
|
42
46
|
var NOTE_MAX_LENGTH = 2e3;
|
|
47
|
+
var DEFAULT_INPUT_MODES = ["text"];
|
|
48
|
+
var DEFAULT_VOICE_PROVIDER = "elevenlabs_scribe_realtime";
|
|
49
|
+
var DEFAULT_VOICE_MODEL_ID = "scribe_v2_realtime";
|
|
43
50
|
var initialModalState = {
|
|
44
51
|
isOpen: false,
|
|
45
52
|
mode: "create",
|
|
@@ -52,10 +59,24 @@ var initialModalState = {
|
|
|
52
59
|
var IssueReportingContext = createContext2(
|
|
53
60
|
void 0
|
|
54
61
|
);
|
|
62
|
+
var IssueReportingPageConfigRegistryContext = createContext2(void 0);
|
|
63
|
+
var hiddenMarkerStyle = {
|
|
64
|
+
position: "absolute",
|
|
65
|
+
width: "1px",
|
|
66
|
+
height: "1px",
|
|
67
|
+
padding: 0,
|
|
68
|
+
margin: "-1px",
|
|
69
|
+
overflow: "hidden",
|
|
70
|
+
clip: "rect(0, 0, 0, 0)",
|
|
71
|
+
whiteSpace: "nowrap",
|
|
72
|
+
border: 0
|
|
73
|
+
};
|
|
55
74
|
var defaultIssueReportingCopy = {
|
|
56
75
|
entryAriaLabel: "Report issue",
|
|
57
76
|
popoverTitle: "Issue Reports",
|
|
58
77
|
reportNewAction: "Report New Issue",
|
|
78
|
+
reportPageAction: "Report This Page",
|
|
79
|
+
reportSpecificAction: "Pick Specific Section",
|
|
59
80
|
filtersAll: "All",
|
|
60
81
|
filtersUnresolved: "Unresolved",
|
|
61
82
|
filtersResolved: "Resolved",
|
|
@@ -72,6 +93,9 @@ var defaultIssueReportingCopy = {
|
|
|
72
93
|
reportModeTitle: "Report mode is active",
|
|
73
94
|
reportModeDescription: "Click a highlighted section to capture the broken surface.",
|
|
74
95
|
reportModeCancelAction: "Cancel",
|
|
96
|
+
generalPageTargetLabel: "Current Page",
|
|
97
|
+
surfaceRequiredDescription: "This page requires selecting a specific section before you submit a report.",
|
|
98
|
+
specificSectionUnavailableDescription: "No specific sections are registered on this page right now.",
|
|
75
99
|
createTitlePrefix: "Report Issue",
|
|
76
100
|
editTitlePrefix: "Edit Issue",
|
|
77
101
|
replyTitlePrefix: "Reply to",
|
|
@@ -115,6 +139,25 @@ function resolveInitialScope(defaultScope, allowTenantScope) {
|
|
|
115
139
|
}
|
|
116
140
|
return "mine";
|
|
117
141
|
}
|
|
142
|
+
function normalizeInputModes(inputModes) {
|
|
143
|
+
if (!inputModes || inputModes.length === 0) {
|
|
144
|
+
return DEFAULT_INPUT_MODES;
|
|
145
|
+
}
|
|
146
|
+
const unique = Array.from(new Set(inputModes));
|
|
147
|
+
const supported = unique.filter(
|
|
148
|
+
(mode) => mode === "text" || mode === "voice"
|
|
149
|
+
);
|
|
150
|
+
if (supported.length === 0) {
|
|
151
|
+
return DEFAULT_INPUT_MODES;
|
|
152
|
+
}
|
|
153
|
+
return supported;
|
|
154
|
+
}
|
|
155
|
+
function resolveDefaultInputMode(defaultInputMode, inputModes) {
|
|
156
|
+
if (defaultInputMode && inputModes.includes(defaultInputMode)) {
|
|
157
|
+
return defaultInputMode;
|
|
158
|
+
}
|
|
159
|
+
return inputModes[0] ?? "text";
|
|
160
|
+
}
|
|
118
161
|
function normalizeTarget(target, getPageUrl) {
|
|
119
162
|
if (typeof target === "string") {
|
|
120
163
|
const normalized = target.trim();
|
|
@@ -136,6 +179,44 @@ function normalizeTarget(target, getPageUrl) {
|
|
|
136
179
|
metadata: target.metadata ?? {}
|
|
137
180
|
};
|
|
138
181
|
}
|
|
182
|
+
function summarizeTarget(target) {
|
|
183
|
+
if (typeof target === "string") {
|
|
184
|
+
const normalized = target.trim();
|
|
185
|
+
return {
|
|
186
|
+
component_key: normalized,
|
|
187
|
+
component_label: normalized,
|
|
188
|
+
surface_ref: null,
|
|
189
|
+
metadata: {}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const key = target.componentKey.trim();
|
|
193
|
+
return {
|
|
194
|
+
component_key: key,
|
|
195
|
+
component_label: (target.componentLabel ?? key).trim(),
|
|
196
|
+
surface_ref: target.surfaceRef ?? null,
|
|
197
|
+
metadata: target.metadata ?? {}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function isElementVisibleForReporting(element) {
|
|
201
|
+
if (!element || typeof window === "undefined") {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
let current = element;
|
|
205
|
+
while (current) {
|
|
206
|
+
if (current.getAttribute("aria-hidden") === "true") {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
if (current instanceof HTMLElement && (current.hidden || current.inert)) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
const computedStyle = window.getComputedStyle(current);
|
|
213
|
+
if (computedStyle.display === "none" || computedStyle.visibility === "hidden" || computedStyle.visibility === "collapse") {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
current = current.parentElement;
|
|
217
|
+
}
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
139
220
|
function normalizeIssueTarget(issue) {
|
|
140
221
|
return {
|
|
141
222
|
component_key: issue.target.component_key,
|
|
@@ -217,6 +298,23 @@ function getIssueNoteLengthMessage(note, copy) {
|
|
|
217
298
|
}
|
|
218
299
|
return `${note.length}/${NOTE_MAX_LENGTH}`;
|
|
219
300
|
}
|
|
301
|
+
function buildGeneralPageTarget(copy, registeredTargets, getPageUrl) {
|
|
302
|
+
return {
|
|
303
|
+
component_key: "general_page",
|
|
304
|
+
component_label: copy.generalPageTargetLabel,
|
|
305
|
+
page_url: resolvePageUrl(getPageUrl),
|
|
306
|
+
surface_ref: null,
|
|
307
|
+
metadata: {
|
|
308
|
+
capture_mode: "general_page",
|
|
309
|
+
registered_target_count: registeredTargets.length,
|
|
310
|
+
...registeredTargets.length > 0 ? {
|
|
311
|
+
registered_targets: registeredTargets.map(
|
|
312
|
+
({ target }) => summarizeTarget(target)
|
|
313
|
+
)
|
|
314
|
+
} : {}
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
220
318
|
function useIssueReporting() {
|
|
221
319
|
const context = useContext2(IssueReportingContext);
|
|
222
320
|
if (!context) {
|
|
@@ -226,6 +324,25 @@ function useIssueReporting() {
|
|
|
226
324
|
}
|
|
227
325
|
return context;
|
|
228
326
|
}
|
|
327
|
+
function IssueReportingPageConfig({
|
|
328
|
+
createMode
|
|
329
|
+
}) {
|
|
330
|
+
const registry = useContext2(IssueReportingPageConfigRegistryContext);
|
|
331
|
+
const configId = useRef(/* @__PURE__ */ Symbol("issue-reporting-page-config"));
|
|
332
|
+
const markerRef = useRef(null);
|
|
333
|
+
if (!registry) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
"IssueReportingPageConfig must be used within an IssueReportingProvider"
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
registry.registerPageConfig(configId.current, createMode, () => markerRef.current);
|
|
340
|
+
return () => {
|
|
341
|
+
registry.unregisterPageConfig(configId.current);
|
|
342
|
+
};
|
|
343
|
+
}, [createMode, registry]);
|
|
344
|
+
return /* @__PURE__ */ jsx("span", { ref: markerRef, style: hiddenMarkerStyle });
|
|
345
|
+
}
|
|
229
346
|
function useIssueReportingStatus() {
|
|
230
347
|
const { client, isEligible, scope } = useIssueReporting();
|
|
231
348
|
return useQuery({
|
|
@@ -298,6 +415,10 @@ function IssueReportingProvider({
|
|
|
298
415
|
getPageUrl,
|
|
299
416
|
defaultScope,
|
|
300
417
|
allowTenantScope = false,
|
|
418
|
+
defaultCreateMode = "general_page",
|
|
419
|
+
inputModes,
|
|
420
|
+
defaultInputMode,
|
|
421
|
+
voice,
|
|
301
422
|
copy,
|
|
302
423
|
children
|
|
303
424
|
}) {
|
|
@@ -315,6 +436,27 @@ function IssueReportingProvider({
|
|
|
315
436
|
const [scope, setScope] = useState(
|
|
316
437
|
() => resolveInitialScope(defaultScope, allowTenantScope)
|
|
317
438
|
);
|
|
439
|
+
const [pageConfigs, setPageConfigs] = useState([]);
|
|
440
|
+
const [registeredTargets, setRegisteredTargets] = useState(
|
|
441
|
+
[]
|
|
442
|
+
);
|
|
443
|
+
const resolvedInputModes = useMemo(
|
|
444
|
+
() => normalizeInputModes(inputModes),
|
|
445
|
+
[inputModes]
|
|
446
|
+
);
|
|
447
|
+
const resolvedDefaultInputMode = useMemo(
|
|
448
|
+
() => resolveDefaultInputMode(defaultInputMode, resolvedInputModes),
|
|
449
|
+
[defaultInputMode, resolvedInputModes]
|
|
450
|
+
);
|
|
451
|
+
const resolvedVoiceConfig = useMemo(
|
|
452
|
+
() => ({
|
|
453
|
+
provider: voice?.provider ?? DEFAULT_VOICE_PROVIDER,
|
|
454
|
+
modelId: voice?.modelId ?? DEFAULT_VOICE_MODEL_ID,
|
|
455
|
+
requireMicrophonePermission: voice?.requireMicrophonePermission ?? true,
|
|
456
|
+
microphone: voice?.microphone
|
|
457
|
+
}),
|
|
458
|
+
[voice]
|
|
459
|
+
);
|
|
318
460
|
useEffect(() => {
|
|
319
461
|
setScope(resolveInitialScope(defaultScope, allowTenantScope));
|
|
320
462
|
}, [allowTenantScope, defaultScope]);
|
|
@@ -324,6 +466,51 @@ function IssueReportingProvider({
|
|
|
324
466
|
const openPopover = useCallback(() => {
|
|
325
467
|
setIsPopoverOpen(true);
|
|
326
468
|
}, []);
|
|
469
|
+
const openCreateModal = useCallback((target) => {
|
|
470
|
+
setIsPopoverOpen(false);
|
|
471
|
+
setIsReportMode(false);
|
|
472
|
+
setModalState({
|
|
473
|
+
isOpen: true,
|
|
474
|
+
mode: "create",
|
|
475
|
+
issueReportId: null,
|
|
476
|
+
issue: null,
|
|
477
|
+
target,
|
|
478
|
+
error: null,
|
|
479
|
+
isHydrating: false
|
|
480
|
+
});
|
|
481
|
+
}, []);
|
|
482
|
+
const registerPageConfig = useCallback(
|
|
483
|
+
(id, createMode2, getElement) => {
|
|
484
|
+
setPageConfigs((current) => [
|
|
485
|
+
...current.filter((entry) => entry.id !== id),
|
|
486
|
+
{ id, createMode: createMode2, getElement }
|
|
487
|
+
]);
|
|
488
|
+
},
|
|
489
|
+
[]
|
|
490
|
+
);
|
|
491
|
+
const unregisterPageConfig = useCallback((id) => {
|
|
492
|
+
setPageConfigs((current) => current.filter((entry) => entry.id !== id));
|
|
493
|
+
}, []);
|
|
494
|
+
const registerTarget = useCallback(
|
|
495
|
+
(id, target, getElement) => {
|
|
496
|
+
setRegisteredTargets((current) => [
|
|
497
|
+
...current.filter((entry) => entry.id !== id),
|
|
498
|
+
{ id, target, getElement }
|
|
499
|
+
]);
|
|
500
|
+
},
|
|
501
|
+
[]
|
|
502
|
+
);
|
|
503
|
+
const unregisterTarget = useCallback((id) => {
|
|
504
|
+
setRegisteredTargets((current) => current.filter((entry) => entry.id !== id));
|
|
505
|
+
}, []);
|
|
506
|
+
const visiblePageConfigs = pageConfigs.filter(
|
|
507
|
+
({ getElement }) => isElementVisibleForReporting(getElement())
|
|
508
|
+
);
|
|
509
|
+
const visibleRegisteredTargets = registeredTargets.filter(
|
|
510
|
+
({ getElement }) => isElementVisibleForReporting(getElement())
|
|
511
|
+
);
|
|
512
|
+
const createMode = visiblePageConfigs.length > 0 ? visiblePageConfigs[visiblePageConfigs.length - 1].createMode : defaultCreateMode;
|
|
513
|
+
const hasRegisteredTargets = visibleRegisteredTargets.length > 0;
|
|
327
514
|
const enterReportMode = useCallback(() => {
|
|
328
515
|
setIsReportMode(true);
|
|
329
516
|
setIsPopoverOpen(false);
|
|
@@ -335,6 +522,42 @@ function IssueReportingProvider({
|
|
|
335
522
|
setModalState(initialModalState);
|
|
336
523
|
setIsPopoverOpen(true);
|
|
337
524
|
}, []);
|
|
525
|
+
const openPageIssueModal = useCallback(() => {
|
|
526
|
+
if (!isEligible) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
openCreateModal(
|
|
530
|
+
buildGeneralPageTarget(mergedCopy, visibleRegisteredTargets, getPageUrl)
|
|
531
|
+
);
|
|
532
|
+
}, [
|
|
533
|
+
getPageUrl,
|
|
534
|
+
isEligible,
|
|
535
|
+
mergedCopy,
|
|
536
|
+
openCreateModal,
|
|
537
|
+
visibleRegisteredTargets
|
|
538
|
+
]);
|
|
539
|
+
const startNewIssue = useCallback(() => {
|
|
540
|
+
if (!isEligible) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (createMode === "surface_required") {
|
|
544
|
+
if (hasRegisteredTargets) {
|
|
545
|
+
enterReportMode();
|
|
546
|
+
}
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (createMode === "surface_preferred" && hasRegisteredTargets) {
|
|
550
|
+
enterReportMode();
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
openPageIssueModal();
|
|
554
|
+
}, [
|
|
555
|
+
createMode,
|
|
556
|
+
enterReportMode,
|
|
557
|
+
hasRegisteredTargets,
|
|
558
|
+
isEligible,
|
|
559
|
+
openPageIssueModal
|
|
560
|
+
]);
|
|
338
561
|
const hydrateIssueIntoModal = useCallback(
|
|
339
562
|
async (issueReportId, mode) => {
|
|
340
563
|
setModalState({
|
|
@@ -393,18 +616,9 @@ function IssueReportingProvider({
|
|
|
393
616
|
return;
|
|
394
617
|
}
|
|
395
618
|
const normalizedTarget = normalizeTarget(target, getPageUrl);
|
|
396
|
-
|
|
397
|
-
setModalState({
|
|
398
|
-
isOpen: true,
|
|
399
|
-
mode: "create",
|
|
400
|
-
issueReportId: null,
|
|
401
|
-
issue: null,
|
|
402
|
-
target: normalizedTarget,
|
|
403
|
-
error: null,
|
|
404
|
-
isHydrating: false
|
|
405
|
-
});
|
|
619
|
+
openCreateModal(normalizedTarget);
|
|
406
620
|
},
|
|
407
|
-
[getPageUrl, isEligible]
|
|
621
|
+
[getPageUrl, isEligible, openCreateModal]
|
|
408
622
|
);
|
|
409
623
|
const contextValue = useMemo(
|
|
410
624
|
() => ({
|
|
@@ -414,9 +628,12 @@ function IssueReportingProvider({
|
|
|
414
628
|
principalId: principalId ?? null,
|
|
415
629
|
copy: mergedCopy,
|
|
416
630
|
isReportMode,
|
|
631
|
+
hasRegisteredTargets,
|
|
417
632
|
enterReportMode,
|
|
418
633
|
cancelReportMode,
|
|
419
634
|
selectPanel,
|
|
635
|
+
openPageIssueModal,
|
|
636
|
+
startNewIssue,
|
|
420
637
|
isPopoverOpen,
|
|
421
638
|
openPopover,
|
|
422
639
|
closePopover,
|
|
@@ -426,7 +643,11 @@ function IssueReportingProvider({
|
|
|
426
643
|
retryModalHydration,
|
|
427
644
|
scope,
|
|
428
645
|
setScope,
|
|
429
|
-
allowTenantScope
|
|
646
|
+
allowTenantScope,
|
|
647
|
+
createMode,
|
|
648
|
+
inputModes: resolvedInputModes,
|
|
649
|
+
defaultInputMode: resolvedDefaultInputMode,
|
|
650
|
+
voice: resolvedVoiceConfig
|
|
430
651
|
}),
|
|
431
652
|
[
|
|
432
653
|
allowTenantScope,
|
|
@@ -434,30 +655,60 @@ function IssueReportingProvider({
|
|
|
434
655
|
client,
|
|
435
656
|
closeModal,
|
|
436
657
|
closePopover,
|
|
658
|
+
createMode,
|
|
437
659
|
enterReportMode,
|
|
660
|
+
hasRegisteredTargets,
|
|
438
661
|
isEligible,
|
|
439
662
|
isPopoverOpen,
|
|
440
663
|
isReportMode,
|
|
441
664
|
mergedCopy,
|
|
442
665
|
modalState,
|
|
443
666
|
openExistingIssueModal,
|
|
667
|
+
openPageIssueModal,
|
|
444
668
|
openPopover,
|
|
445
669
|
principalId,
|
|
446
670
|
reporterRoleHint,
|
|
447
671
|
retryModalHydration,
|
|
672
|
+
resolvedDefaultInputMode,
|
|
673
|
+
resolvedInputModes,
|
|
674
|
+
resolvedVoiceConfig,
|
|
448
675
|
scope,
|
|
449
|
-
selectPanel
|
|
676
|
+
selectPanel,
|
|
677
|
+
startNewIssue
|
|
450
678
|
]
|
|
451
679
|
);
|
|
452
680
|
const reportModeValue = useMemo(
|
|
453
681
|
() => ({
|
|
454
682
|
isReportMode,
|
|
455
683
|
selectPanel,
|
|
456
|
-
cancelReportMode
|
|
684
|
+
cancelReportMode,
|
|
685
|
+
hasRegisteredTargets,
|
|
686
|
+
registerTarget,
|
|
687
|
+
unregisterTarget
|
|
457
688
|
}),
|
|
458
|
-
[
|
|
689
|
+
[
|
|
690
|
+
cancelReportMode,
|
|
691
|
+
hasRegisteredTargets,
|
|
692
|
+
isReportMode,
|
|
693
|
+
registerTarget,
|
|
694
|
+
selectPanel,
|
|
695
|
+
unregisterTarget
|
|
696
|
+
]
|
|
697
|
+
);
|
|
698
|
+
const pageConfigRegistryValue = useMemo(
|
|
699
|
+
() => ({
|
|
700
|
+
registerPageConfig,
|
|
701
|
+
unregisterPageConfig
|
|
702
|
+
}),
|
|
703
|
+
[registerPageConfig, unregisterPageConfig]
|
|
704
|
+
);
|
|
705
|
+
return /* @__PURE__ */ jsx(
|
|
706
|
+
IssueReportingPageConfigRegistryContext.Provider,
|
|
707
|
+
{
|
|
708
|
+
value: pageConfigRegistryValue,
|
|
709
|
+
children: /* @__PURE__ */ jsx(ReportModeContext.Provider, { value: reportModeValue, children: /* @__PURE__ */ jsx(IssueReportingContext.Provider, { value: contextValue, children }) })
|
|
710
|
+
}
|
|
459
711
|
);
|
|
460
|
-
return /* @__PURE__ */ jsx(ReportModeContext.Provider, { value: reportModeValue, children: /* @__PURE__ */ jsx(IssueReportingContext.Provider, { value: contextValue, children }) });
|
|
461
712
|
}
|
|
462
713
|
|
|
463
714
|
// src/components.tsx
|
|
@@ -465,6 +716,18 @@ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
|
465
716
|
function cn(...values) {
|
|
466
717
|
return values.filter(Boolean).join(" ");
|
|
467
718
|
}
|
|
719
|
+
var Z_FLOATING_BUTTON = "z-[65]";
|
|
720
|
+
var Z_BANNER = "z-[70]";
|
|
721
|
+
var Z_POPOVER = "z-[70]";
|
|
722
|
+
var Z_MODAL_OVERLAY = "z-[80]";
|
|
723
|
+
var Z_MODAL_CONTENT = "z-[81]";
|
|
724
|
+
var POPOVER_WIDTH = "w-[360px]";
|
|
725
|
+
var MODAL_WIDTH = "w-[calc(100vw-2rem)]";
|
|
726
|
+
var MODAL_RADIUS = "rounded-[28px]";
|
|
727
|
+
var POPOVER_SHADOW = "shadow-[0_18px_48px_rgba(15,23,42,0.18)]";
|
|
728
|
+
var MODAL_SHADOW = "shadow-[0_28px_80px_rgba(15,23,42,0.24)]";
|
|
729
|
+
var BADGE_TEXT = "text-[11px]";
|
|
730
|
+
var LABEL_TEXT = "text-[11px]";
|
|
468
731
|
function truncate(value, max = 80) {
|
|
469
732
|
if (value.length <= max) {
|
|
470
733
|
return value;
|
|
@@ -485,6 +748,16 @@ function resolveErrorMessage(error, fallback) {
|
|
|
485
748
|
}
|
|
486
749
|
return fallback;
|
|
487
750
|
}
|
|
751
|
+
function getCommittedTranscriptText(committedTranscripts) {
|
|
752
|
+
return committedTranscripts.map((segment) => segment.text.trim()).filter(Boolean).join(" ").trim();
|
|
753
|
+
}
|
|
754
|
+
function appendTranscriptToNote(current, transcript) {
|
|
755
|
+
const normalizedCurrent = current.trim();
|
|
756
|
+
if (!normalizedCurrent) {
|
|
757
|
+
return transcript;
|
|
758
|
+
}
|
|
759
|
+
return `${normalizedCurrent} ${transcript}`;
|
|
760
|
+
}
|
|
488
761
|
function resolveReporterId(issue) {
|
|
489
762
|
return issue.reporter_principal_id ?? issue.reporter_user_id ?? null;
|
|
490
763
|
}
|
|
@@ -517,13 +790,390 @@ function getIssueOriginText(issue, copy) {
|
|
|
517
790
|
const humanName = issue.reporter_display_name?.trim();
|
|
518
791
|
return humanName ? `${copy.originHumanLabel} \xB7 ${humanName}` : copy.originHumanLabel;
|
|
519
792
|
}
|
|
793
|
+
function IssueReportVoicePanel({
|
|
794
|
+
canUseText,
|
|
795
|
+
effectiveInputMode,
|
|
796
|
+
isSubmitting,
|
|
797
|
+
isVoiceActive,
|
|
798
|
+
isVoiceConnecting,
|
|
799
|
+
voice,
|
|
800
|
+
voiceTokenResult,
|
|
801
|
+
committedTranscript,
|
|
802
|
+
voiceError,
|
|
803
|
+
scribeError,
|
|
804
|
+
onSelectText,
|
|
805
|
+
onSelectVoice,
|
|
806
|
+
onStartVoiceInput,
|
|
807
|
+
onStopVoiceInput,
|
|
808
|
+
onAppendTranscript
|
|
809
|
+
}) {
|
|
810
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
811
|
+
canUseText ? /* @__PURE__ */ jsxs("div", { className: "mt-5 flex gap-2", children: [
|
|
812
|
+
/* @__PURE__ */ jsxs(
|
|
813
|
+
"button",
|
|
814
|
+
{
|
|
815
|
+
type: "button",
|
|
816
|
+
className: cn(
|
|
817
|
+
"inline-flex items-center gap-1 rounded-full border px-3 py-1.5 text-xs font-semibold transition",
|
|
818
|
+
effectiveInputMode === "text" ? "border-slate-900 bg-slate-900 text-white" : "border-slate-200 text-slate-700 hover:bg-slate-50"
|
|
819
|
+
),
|
|
820
|
+
onClick: onSelectText,
|
|
821
|
+
disabled: isSubmitting,
|
|
822
|
+
children: [
|
|
823
|
+
/* @__PURE__ */ jsx2(TextT, { className: "h-3.5 w-3.5" }),
|
|
824
|
+
"Text Input"
|
|
825
|
+
]
|
|
826
|
+
}
|
|
827
|
+
),
|
|
828
|
+
/* @__PURE__ */ jsxs(
|
|
829
|
+
"button",
|
|
830
|
+
{
|
|
831
|
+
type: "button",
|
|
832
|
+
className: cn(
|
|
833
|
+
"inline-flex items-center gap-1 rounded-full border px-3 py-1.5 text-xs font-semibold transition",
|
|
834
|
+
effectiveInputMode === "voice" ? "border-slate-900 bg-slate-900 text-white" : "border-slate-200 text-slate-700 hover:bg-slate-50"
|
|
835
|
+
),
|
|
836
|
+
onClick: onSelectVoice,
|
|
837
|
+
disabled: isSubmitting,
|
|
838
|
+
children: [
|
|
839
|
+
/* @__PURE__ */ jsx2(Microphone, { className: "h-3.5 w-3.5" }),
|
|
840
|
+
"Voice Input"
|
|
841
|
+
]
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
] }) : null,
|
|
845
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3", children: [
|
|
846
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
847
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
848
|
+
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium uppercase tracking-wide text-slate-500", children: "Voice Input" }),
|
|
849
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-slate-600", children: [
|
|
850
|
+
"Provider: ",
|
|
851
|
+
voiceTokenResult?.provider ?? voice.provider,
|
|
852
|
+
" \xB7 Model:",
|
|
853
|
+
" ",
|
|
854
|
+
voiceTokenResult?.model_id ?? voice.modelId
|
|
855
|
+
] }),
|
|
856
|
+
/* @__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." })
|
|
857
|
+
] }),
|
|
858
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
859
|
+
isVoiceActive || isVoiceConnecting ? /* @__PURE__ */ jsx2(
|
|
860
|
+
"button",
|
|
861
|
+
{
|
|
862
|
+
type: "button",
|
|
863
|
+
className: "rounded-full border border-slate-300 px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-100",
|
|
864
|
+
onClick: onStopVoiceInput,
|
|
865
|
+
disabled: isSubmitting,
|
|
866
|
+
children: "Stop Voice Input"
|
|
867
|
+
}
|
|
868
|
+
) : /* @__PURE__ */ jsx2(
|
|
869
|
+
"button",
|
|
870
|
+
{
|
|
871
|
+
type: "button",
|
|
872
|
+
className: "rounded-full bg-slate-900 px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
873
|
+
onClick: onStartVoiceInput,
|
|
874
|
+
disabled: isSubmitting,
|
|
875
|
+
children: "Start Voice Input"
|
|
876
|
+
}
|
|
877
|
+
),
|
|
878
|
+
canUseText ? /* @__PURE__ */ jsx2(
|
|
879
|
+
"button",
|
|
880
|
+
{
|
|
881
|
+
type: "button",
|
|
882
|
+
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",
|
|
883
|
+
onClick: onAppendTranscript,
|
|
884
|
+
disabled: isSubmitting || !committedTranscript.trim(),
|
|
885
|
+
children: "Append Transcript"
|
|
886
|
+
}
|
|
887
|
+
) : null
|
|
888
|
+
] })
|
|
889
|
+
] }),
|
|
890
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-3 rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700", children: committedTranscript ? committedTranscript : /* @__PURE__ */ jsx2("span", { className: "text-slate-500", children: "No committed transcript yet." }) }),
|
|
891
|
+
voiceError || scribeError ? /* @__PURE__ */ jsx2("div", { className: "mt-2 rounded-xl border border-rose-200 bg-rose-50 px-3 py-2 text-xs text-rose-700", children: voiceError ?? scribeError }) : isVoiceConnecting ? /* @__PURE__ */ jsx2("div", { className: "mt-2 text-xs text-slate-500", children: "Connecting voice transcription..." }) : null
|
|
892
|
+
] })
|
|
893
|
+
] });
|
|
894
|
+
}
|
|
895
|
+
function IssueReportNoteEditor({
|
|
896
|
+
canUseText,
|
|
897
|
+
note,
|
|
898
|
+
normalizedNote,
|
|
899
|
+
isSubmitting,
|
|
900
|
+
copy,
|
|
901
|
+
onNoteChange,
|
|
902
|
+
onSubmit
|
|
903
|
+
}) {
|
|
904
|
+
if (!canUseText) {
|
|
905
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-5 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-600", children: [
|
|
906
|
+
/* @__PURE__ */ jsx2("div", { children: getIssueNoteLengthMessage(normalizedNote, copy) }),
|
|
907
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-1", children: "Submit is enabled after a committed transcript reaches 10-2000 characters." })
|
|
908
|
+
] });
|
|
909
|
+
}
|
|
910
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-5 space-y-2", children: [
|
|
911
|
+
/* @__PURE__ */ jsx2(
|
|
912
|
+
"textarea",
|
|
913
|
+
{
|
|
914
|
+
value: note,
|
|
915
|
+
onChange: (event) => onNoteChange(event.target.value),
|
|
916
|
+
onKeyDown: (event) => {
|
|
917
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
918
|
+
event.preventDefault();
|
|
919
|
+
onSubmit();
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
placeholder: copy.notePlaceholder,
|
|
923
|
+
className: "h-36 w-full resize-none rounded-2xl border border-slate-300 px-4 py-3 text-sm text-slate-800 outline-none transition focus:border-slate-500 focus:ring-2 focus:ring-slate-200",
|
|
924
|
+
disabled: isSubmitting,
|
|
925
|
+
autoFocus: true
|
|
926
|
+
}
|
|
927
|
+
),
|
|
928
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs text-slate-500", children: [
|
|
929
|
+
/* @__PURE__ */ jsx2("span", { children: getIssueNoteLengthMessage(note, copy) }),
|
|
930
|
+
/* @__PURE__ */ jsx2("span", { children: copy.keyboardShortcutHint })
|
|
931
|
+
] })
|
|
932
|
+
] });
|
|
933
|
+
}
|
|
934
|
+
function IssueReportModalDescription({
|
|
935
|
+
copy,
|
|
936
|
+
mode,
|
|
937
|
+
target
|
|
938
|
+
}) {
|
|
939
|
+
if (mode === "create" && target) {
|
|
940
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
941
|
+
copy.createDescriptionPrefix,
|
|
942
|
+
" ",
|
|
943
|
+
/* @__PURE__ */ jsx2("code", { className: "rounded bg-slate-100 px-1.5 py-0.5 text-xs text-slate-700", children: target.page_url })
|
|
944
|
+
] });
|
|
945
|
+
}
|
|
946
|
+
return mode === "edit" ? copy.editDescription : copy.replyDescription;
|
|
947
|
+
}
|
|
948
|
+
function IssueReportModalBody({
|
|
949
|
+
copy,
|
|
950
|
+
mode,
|
|
951
|
+
issue,
|
|
952
|
+
isHydrating,
|
|
953
|
+
error,
|
|
954
|
+
canUseVoice,
|
|
955
|
+
canUseText,
|
|
956
|
+
effectiveInputMode,
|
|
957
|
+
note,
|
|
958
|
+
normalizedNote,
|
|
959
|
+
isValid,
|
|
960
|
+
isSubmitting,
|
|
961
|
+
isVoiceActive,
|
|
962
|
+
isVoiceConnecting,
|
|
963
|
+
voice,
|
|
964
|
+
voiceTokenResult,
|
|
965
|
+
committedTranscript,
|
|
966
|
+
voiceError,
|
|
967
|
+
scribeError,
|
|
968
|
+
submitError,
|
|
969
|
+
onRetryHydration,
|
|
970
|
+
onClose,
|
|
971
|
+
onSelectText,
|
|
972
|
+
onSelectVoice,
|
|
973
|
+
onStartVoiceInput,
|
|
974
|
+
onStopVoiceInput,
|
|
975
|
+
onAppendTranscript,
|
|
976
|
+
onNoteChange,
|
|
977
|
+
onSubmit
|
|
978
|
+
}) {
|
|
979
|
+
if (isHydrating) {
|
|
980
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-5 flex items-center gap-2 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-4 text-sm text-slate-600", children: [
|
|
981
|
+
/* @__PURE__ */ jsx2(Spinner, { className: "h-4 w-4 animate-spin" }),
|
|
982
|
+
/* @__PURE__ */ jsx2("span", { children: copy.hydrateLoading })
|
|
983
|
+
] });
|
|
984
|
+
}
|
|
985
|
+
if (error) {
|
|
986
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-5 space-y-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-4 text-sm text-rose-700", children: [
|
|
987
|
+
/* @__PURE__ */ jsx2("div", { children: error }),
|
|
988
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
989
|
+
/* @__PURE__ */ jsx2(
|
|
990
|
+
"button",
|
|
991
|
+
{
|
|
992
|
+
type: "button",
|
|
993
|
+
className: "rounded-full border border-rose-300 px-3 py-1 font-medium transition hover:bg-rose-100",
|
|
994
|
+
onClick: onRetryHydration,
|
|
995
|
+
children: copy.retryAction
|
|
996
|
+
}
|
|
997
|
+
),
|
|
998
|
+
/* @__PURE__ */ jsx2(
|
|
999
|
+
"button",
|
|
1000
|
+
{
|
|
1001
|
+
type: "button",
|
|
1002
|
+
className: "rounded-full border border-slate-300 px-3 py-1 font-medium text-slate-700 transition hover:bg-slate-50",
|
|
1003
|
+
onClick: onClose,
|
|
1004
|
+
children: copy.cancelAction
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
] })
|
|
1008
|
+
] });
|
|
1009
|
+
}
|
|
1010
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1011
|
+
mode === "create" && canUseVoice ? /* @__PURE__ */ jsx2(
|
|
1012
|
+
IssueReportVoicePanel,
|
|
1013
|
+
{
|
|
1014
|
+
canUseText,
|
|
1015
|
+
effectiveInputMode,
|
|
1016
|
+
isSubmitting,
|
|
1017
|
+
isVoiceActive,
|
|
1018
|
+
isVoiceConnecting,
|
|
1019
|
+
voice,
|
|
1020
|
+
voiceTokenResult,
|
|
1021
|
+
committedTranscript,
|
|
1022
|
+
voiceError,
|
|
1023
|
+
scribeError,
|
|
1024
|
+
onSelectText,
|
|
1025
|
+
onSelectVoice,
|
|
1026
|
+
onStartVoiceInput,
|
|
1027
|
+
onStopVoiceInput,
|
|
1028
|
+
onAppendTranscript
|
|
1029
|
+
}
|
|
1030
|
+
) : null,
|
|
1031
|
+
mode === "reply" && issue ? /* @__PURE__ */ jsxs("div", { className: "mt-5 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3", children: [
|
|
1032
|
+
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium uppercase tracking-wide text-slate-500", children: copy.originalIssueLabel }),
|
|
1033
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm text-slate-700", children: issue.note })
|
|
1034
|
+
] }) : null,
|
|
1035
|
+
/* @__PURE__ */ jsx2(
|
|
1036
|
+
IssueReportNoteEditor,
|
|
1037
|
+
{
|
|
1038
|
+
canUseText,
|
|
1039
|
+
note,
|
|
1040
|
+
normalizedNote,
|
|
1041
|
+
isSubmitting,
|
|
1042
|
+
copy,
|
|
1043
|
+
onNoteChange,
|
|
1044
|
+
onSubmit
|
|
1045
|
+
}
|
|
1046
|
+
),
|
|
1047
|
+
submitError ? /* @__PURE__ */ jsx2("div", { className: "mt-4 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700", children: submitError }) : null,
|
|
1048
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex justify-end gap-3", children: [
|
|
1049
|
+
/* @__PURE__ */ jsx2(
|
|
1050
|
+
"button",
|
|
1051
|
+
{
|
|
1052
|
+
type: "button",
|
|
1053
|
+
className: "rounded-full border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50",
|
|
1054
|
+
onClick: onClose,
|
|
1055
|
+
disabled: isSubmitting,
|
|
1056
|
+
children: copy.cancelAction
|
|
1057
|
+
}
|
|
1058
|
+
),
|
|
1059
|
+
/* @__PURE__ */ jsx2(
|
|
1060
|
+
"button",
|
|
1061
|
+
{
|
|
1062
|
+
type: "button",
|
|
1063
|
+
className: cn(
|
|
1064
|
+
"rounded-full px-4 py-2 text-sm font-semibold text-white transition",
|
|
1065
|
+
isValid && !isSubmitting ? "bg-slate-900 hover:bg-slate-800" : "cursor-not-allowed bg-slate-300"
|
|
1066
|
+
),
|
|
1067
|
+
onClick: onSubmit,
|
|
1068
|
+
disabled: !isValid || isSubmitting,
|
|
1069
|
+
children: isSubmitting ? copy.submittingAction : copy.submitAction
|
|
1070
|
+
}
|
|
1071
|
+
)
|
|
1072
|
+
] })
|
|
1073
|
+
] });
|
|
1074
|
+
}
|
|
1075
|
+
function useIssueReportVoiceCapture({
|
|
1076
|
+
canUseVoice,
|
|
1077
|
+
defaultInputMode,
|
|
1078
|
+
isSubmitting,
|
|
1079
|
+
voice
|
|
1080
|
+
}) {
|
|
1081
|
+
const [inputMode, setInputMode] = useState2(defaultInputMode);
|
|
1082
|
+
const [voiceTokenResult, setVoiceTokenResult] = useState2(null);
|
|
1083
|
+
const [voiceSubmitMetadata, setVoiceSubmitMetadata] = useState2(null);
|
|
1084
|
+
const [voiceError, setVoiceError] = useState2(null);
|
|
1085
|
+
const {
|
|
1086
|
+
status: scribeStatus,
|
|
1087
|
+
isConnected,
|
|
1088
|
+
isTranscribing,
|
|
1089
|
+
committedTranscripts,
|
|
1090
|
+
error: scribeError,
|
|
1091
|
+
connect: connectScribe,
|
|
1092
|
+
disconnect: disconnectScribe
|
|
1093
|
+
} = useScribe({
|
|
1094
|
+
autoConnect: false,
|
|
1095
|
+
modelId: voice.modelId,
|
|
1096
|
+
microphone: voice.microphone
|
|
1097
|
+
});
|
|
1098
|
+
const committedTranscript = useMemo2(
|
|
1099
|
+
() => getCommittedTranscriptText(committedTranscripts),
|
|
1100
|
+
[committedTranscripts]
|
|
1101
|
+
);
|
|
1102
|
+
const isVoiceConnecting = scribeStatus === "connecting";
|
|
1103
|
+
const isVoiceActive = isConnected || isTranscribing;
|
|
1104
|
+
const resetVoiceCapture = useCallback2(() => {
|
|
1105
|
+
disconnectScribe();
|
|
1106
|
+
setInputMode(defaultInputMode);
|
|
1107
|
+
setVoiceTokenResult(null);
|
|
1108
|
+
setVoiceSubmitMetadata(null);
|
|
1109
|
+
setVoiceError(null);
|
|
1110
|
+
}, [defaultInputMode, disconnectScribe]);
|
|
1111
|
+
const startVoiceInput = useCallback2(
|
|
1112
|
+
async (createVoiceToken) => {
|
|
1113
|
+
if (!canUseVoice || isSubmitting || isVoiceActive || isVoiceConnecting) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (!createVoiceToken) {
|
|
1117
|
+
setVoiceError("Voice input is unavailable for this client.");
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
setVoiceError(null);
|
|
1121
|
+
try {
|
|
1122
|
+
const tokenResult = await createVoiceToken();
|
|
1123
|
+
setVoiceTokenResult(tokenResult);
|
|
1124
|
+
setVoiceSubmitMetadata(tokenResult);
|
|
1125
|
+
await connectScribe({
|
|
1126
|
+
token: tokenResult.token,
|
|
1127
|
+
modelId: tokenResult.model_id,
|
|
1128
|
+
microphone: voice.microphone
|
|
1129
|
+
});
|
|
1130
|
+
setInputMode("voice");
|
|
1131
|
+
} catch (startError) {
|
|
1132
|
+
setVoiceError(resolveErrorMessage(startError, "Failed to start voice input."));
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1135
|
+
[
|
|
1136
|
+
canUseVoice,
|
|
1137
|
+
connectScribe,
|
|
1138
|
+
isSubmitting,
|
|
1139
|
+
isVoiceActive,
|
|
1140
|
+
isVoiceConnecting,
|
|
1141
|
+
voice.microphone
|
|
1142
|
+
]
|
|
1143
|
+
);
|
|
1144
|
+
const appendTranscript = useCallback2(
|
|
1145
|
+
(setNote) => {
|
|
1146
|
+
if (!committedTranscript.trim()) {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
setNote((current) => appendTranscriptToNote(current, committedTranscript));
|
|
1150
|
+
setInputMode("text");
|
|
1151
|
+
},
|
|
1152
|
+
[committedTranscript]
|
|
1153
|
+
);
|
|
1154
|
+
return {
|
|
1155
|
+
inputMode,
|
|
1156
|
+
setInputMode,
|
|
1157
|
+
voiceTokenResult,
|
|
1158
|
+
voiceSubmitMetadata,
|
|
1159
|
+
committedTranscript,
|
|
1160
|
+
voiceError,
|
|
1161
|
+
scribeError,
|
|
1162
|
+
isVoiceConnecting,
|
|
1163
|
+
isVoiceActive,
|
|
1164
|
+
resetVoiceCapture,
|
|
1165
|
+
startVoiceInput,
|
|
1166
|
+
stopVoiceInput: disconnectScribe,
|
|
1167
|
+
appendTranscript
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
520
1170
|
function IssueReportModeBanner() {
|
|
521
1171
|
const { copy } = useIssueReporting();
|
|
522
1172
|
const reportMode = useReportMode();
|
|
523
1173
|
if (!reportMode?.isReportMode) {
|
|
524
1174
|
return null;
|
|
525
1175
|
}
|
|
526
|
-
return /* @__PURE__ */ jsx2("div", { className: "fixed inset-x-4 top-4
|
|
1176
|
+
return /* @__PURE__ */ jsx2("div", { className: cn("fixed inset-x-4 top-4 flex justify-center", Z_BANNER), children: /* @__PURE__ */ jsx2("div", { className: "max-w-xl rounded-full border border-amber-300 bg-amber-50/95 px-4 py-3 text-sm text-amber-950 shadow-lg backdrop-blur", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3", children: [
|
|
527
1177
|
/* @__PURE__ */ jsx2("div", { className: "font-medium", children: copy.reportModeTitle }),
|
|
528
1178
|
/* @__PURE__ */ jsx2("div", { className: "text-amber-900/80", children: copy.reportModeDescription }),
|
|
529
1179
|
/* @__PURE__ */ jsx2(
|
|
@@ -586,13 +1236,14 @@ function IssueList({
|
|
|
586
1236
|
"span",
|
|
587
1237
|
{
|
|
588
1238
|
className: cn(
|
|
589
|
-
"rounded-full px-2 py-0.5
|
|
1239
|
+
"rounded-full px-2 py-0.5 font-medium",
|
|
1240
|
+
BADGE_TEXT,
|
|
590
1241
|
getIssueStatusClassName(issue.status)
|
|
591
1242
|
),
|
|
592
1243
|
children: getIssueStatusBadgeLabel(issue.status)
|
|
593
1244
|
}
|
|
594
1245
|
),
|
|
595
|
-
/* @__PURE__ */ jsx2("span", { className: "rounded-full bg-slate-100 px-2 py-0.5
|
|
1246
|
+
/* @__PURE__ */ jsx2("span", { className: cn("rounded-full bg-slate-100 px-2 py-0.5 font-medium text-slate-600", BADGE_TEXT), children: getIssueOriginText(issue, copy) }),
|
|
596
1247
|
/* @__PURE__ */ jsx2("span", { className: "text-xs text-slate-400", children: formatRelativeTime(issue.updated_at) })
|
|
597
1248
|
] })
|
|
598
1249
|
] }),
|
|
@@ -623,10 +1274,13 @@ function IssueReportPopover({ children }) {
|
|
|
623
1274
|
openPopover,
|
|
624
1275
|
closePopover,
|
|
625
1276
|
enterReportMode,
|
|
1277
|
+
openPageIssueModal,
|
|
626
1278
|
openExistingIssueModal,
|
|
627
1279
|
scope,
|
|
628
1280
|
setScope,
|
|
629
|
-
allowTenantScope
|
|
1281
|
+
allowTenantScope,
|
|
1282
|
+
createMode,
|
|
1283
|
+
hasRegisteredTargets
|
|
630
1284
|
} = useIssueReporting();
|
|
631
1285
|
const history = useIssueReportingHistory("all");
|
|
632
1286
|
const status = useIssueReportingStatus();
|
|
@@ -640,6 +1294,9 @@ function IssueReportPopover({ children }) {
|
|
|
640
1294
|
[allItems]
|
|
641
1295
|
);
|
|
642
1296
|
const statusSummary = status.data ? `${status.data.open_count} open \xB7 ${status.data.recent_resolved_count} recently resolved` : "Status reflects the active scope.";
|
|
1297
|
+
const showSectionFirst = createMode === "surface_required" || createMode === "surface_preferred" && hasRegisteredTargets;
|
|
1298
|
+
const sectionActionDisabled = !hasRegisteredTargets;
|
|
1299
|
+
const helperText = createMode === "surface_required" ? hasRegisteredTargets ? copy.surfaceRequiredDescription : copy.specificSectionUnavailableDescription : createMode === "surface_preferred" && !hasRegisteredTargets ? copy.specificSectionUnavailableDescription : null;
|
|
643
1300
|
return /* @__PURE__ */ jsxs(
|
|
644
1301
|
Popover.Root,
|
|
645
1302
|
{
|
|
@@ -653,26 +1310,78 @@ function IssueReportPopover({ children }) {
|
|
|
653
1310
|
align: "end",
|
|
654
1311
|
side: "top",
|
|
655
1312
|
sideOffset: 12,
|
|
656
|
-
className:
|
|
1313
|
+
className: cn(
|
|
1314
|
+
"rounded-3xl border border-slate-200 bg-white p-4",
|
|
1315
|
+
Z_POPOVER,
|
|
1316
|
+
POPOVER_WIDTH,
|
|
1317
|
+
POPOVER_SHADOW
|
|
1318
|
+
),
|
|
657
1319
|
children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
658
1320
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
659
1321
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
660
1322
|
/* @__PURE__ */ jsx2("h3", { className: "text-sm font-semibold text-slate-900", children: copy.popoverTitle }),
|
|
661
1323
|
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-slate-500", children: statusSummary })
|
|
662
1324
|
] }),
|
|
663
|
-
/* @__PURE__ */ jsx2(
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1325
|
+
/* @__PURE__ */ jsx2("div", { className: "flex flex-wrap justify-end gap-2", children: showSectionFirst ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1326
|
+
/* @__PURE__ */ jsx2(
|
|
1327
|
+
"button",
|
|
1328
|
+
{
|
|
1329
|
+
type: "button",
|
|
1330
|
+
className: cn(
|
|
1331
|
+
"rounded-full px-3 py-2 text-xs font-semibold transition",
|
|
1332
|
+
sectionActionDisabled ? "cursor-not-allowed bg-slate-200 text-slate-500" : "bg-slate-900 text-white hover:bg-slate-800"
|
|
1333
|
+
),
|
|
1334
|
+
onClick: () => {
|
|
1335
|
+
if (sectionActionDisabled) {
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
closePopover();
|
|
1339
|
+
enterReportMode();
|
|
1340
|
+
},
|
|
1341
|
+
disabled: sectionActionDisabled,
|
|
1342
|
+
children: createMode === "surface_required" ? copy.reportNewAction : copy.reportSpecificAction
|
|
1343
|
+
}
|
|
1344
|
+
),
|
|
1345
|
+
createMode !== "surface_required" ? /* @__PURE__ */ jsx2(
|
|
1346
|
+
"button",
|
|
1347
|
+
{
|
|
1348
|
+
type: "button",
|
|
1349
|
+
className: "rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
1350
|
+
onClick: () => {
|
|
1351
|
+
closePopover();
|
|
1352
|
+
openPageIssueModal();
|
|
1353
|
+
},
|
|
1354
|
+
children: copy.reportPageAction
|
|
1355
|
+
}
|
|
1356
|
+
) : null
|
|
1357
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1358
|
+
/* @__PURE__ */ jsx2(
|
|
1359
|
+
"button",
|
|
1360
|
+
{
|
|
1361
|
+
type: "button",
|
|
1362
|
+
className: "rounded-full bg-slate-900 px-3 py-2 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
1363
|
+
onClick: () => {
|
|
1364
|
+
closePopover();
|
|
1365
|
+
openPageIssueModal();
|
|
1366
|
+
},
|
|
1367
|
+
children: createMode === "general_page" ? copy.reportPageAction : copy.reportNewAction
|
|
1368
|
+
}
|
|
1369
|
+
),
|
|
1370
|
+
hasRegisteredTargets ? /* @__PURE__ */ jsx2(
|
|
1371
|
+
"button",
|
|
1372
|
+
{
|
|
1373
|
+
type: "button",
|
|
1374
|
+
className: "rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
1375
|
+
onClick: () => {
|
|
1376
|
+
closePopover();
|
|
1377
|
+
enterReportMode();
|
|
1378
|
+
},
|
|
1379
|
+
children: copy.reportSpecificAction
|
|
1380
|
+
}
|
|
1381
|
+
) : null
|
|
1382
|
+
] }) })
|
|
675
1383
|
] }),
|
|
1384
|
+
helperText ? /* @__PURE__ */ jsx2("div", { className: "rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-900", children: helperText }) : null,
|
|
676
1385
|
status.error ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-4 text-sm text-rose-700", children: [
|
|
677
1386
|
/* @__PURE__ */ jsx2("div", { children: status.error.message || copy.statusLoadFailed }),
|
|
678
1387
|
/* @__PURE__ */ jsx2(
|
|
@@ -686,7 +1395,7 @@ function IssueReportPopover({ children }) {
|
|
|
686
1395
|
)
|
|
687
1396
|
] }) : null,
|
|
688
1397
|
allowTenantScope ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
689
|
-
/* @__PURE__ */ jsx2("div", { className: "
|
|
1398
|
+
/* @__PURE__ */ jsx2("div", { className: cn("font-medium uppercase tracking-wide text-slate-500", LABEL_TEXT), children: copy.scopeLabel }),
|
|
690
1399
|
/* @__PURE__ */ jsx2("div", { className: "flex gap-2", children: [
|
|
691
1400
|
["tenant", copy.scopeTenant],
|
|
692
1401
|
["mine", copy.scopeMine]
|
|
@@ -742,18 +1451,46 @@ function IssueReportPopover({ children }) {
|
|
|
742
1451
|
}
|
|
743
1452
|
function IssueReportModal() {
|
|
744
1453
|
const {
|
|
1454
|
+
client,
|
|
745
1455
|
copy,
|
|
746
1456
|
reporterRoleHint,
|
|
747
1457
|
modalState,
|
|
748
1458
|
closeModal,
|
|
749
|
-
retryModalHydration
|
|
1459
|
+
retryModalHydration,
|
|
1460
|
+
inputModes,
|
|
1461
|
+
defaultInputMode,
|
|
1462
|
+
voice
|
|
750
1463
|
} = useIssueReporting();
|
|
751
1464
|
const { createMutation, updateMutation, replyMutation } = useIssueReportingMutations();
|
|
752
1465
|
const [note, setNote] = useState2("");
|
|
753
1466
|
const [submitError, setSubmitError] = useState2(null);
|
|
754
1467
|
const { isOpen, mode, issue, target, isHydrating, error } = modalState;
|
|
1468
|
+
const canUseVoice = mode === "create" && inputModes.includes("voice");
|
|
1469
|
+
const canUseText = mode !== "create" || inputModes.includes("text");
|
|
1470
|
+
const isSubmitting = createMutation.isPending || updateMutation.isPending || replyMutation.isPending;
|
|
1471
|
+
const {
|
|
1472
|
+
inputMode,
|
|
1473
|
+
setInputMode,
|
|
1474
|
+
voiceTokenResult,
|
|
1475
|
+
voiceSubmitMetadata,
|
|
1476
|
+
committedTranscript,
|
|
1477
|
+
voiceError,
|
|
1478
|
+
scribeError,
|
|
1479
|
+
isVoiceConnecting,
|
|
1480
|
+
isVoiceActive,
|
|
1481
|
+
resetVoiceCapture,
|
|
1482
|
+
startVoiceInput,
|
|
1483
|
+
stopVoiceInput,
|
|
1484
|
+
appendTranscript
|
|
1485
|
+
} = useIssueReportVoiceCapture({
|
|
1486
|
+
canUseVoice,
|
|
1487
|
+
defaultInputMode,
|
|
1488
|
+
isSubmitting,
|
|
1489
|
+
voice
|
|
1490
|
+
});
|
|
755
1491
|
useEffect2(() => {
|
|
756
1492
|
if (!isOpen) {
|
|
1493
|
+
resetVoiceCapture();
|
|
757
1494
|
setNote("");
|
|
758
1495
|
setSubmitError(null);
|
|
759
1496
|
return;
|
|
@@ -763,140 +1500,145 @@ function IssueReportModal() {
|
|
|
763
1500
|
} else {
|
|
764
1501
|
setNote("");
|
|
765
1502
|
}
|
|
1503
|
+
resetVoiceCapture();
|
|
766
1504
|
setSubmitError(null);
|
|
767
|
-
}, [isOpen, mode,
|
|
768
|
-
const
|
|
769
|
-
const
|
|
1505
|
+
}, [isOpen, issue, mode, resetVoiceCapture]);
|
|
1506
|
+
const effectiveInputMode = mode !== "create" ? "text" : canUseVoice && inputMode === "voice" ? "voice" : "text";
|
|
1507
|
+
const normalizedNote = effectiveInputMode === "voice" ? committedTranscript.trim() : note.trim();
|
|
1508
|
+
const isValid = normalizedNote.length >= 10 && normalizedNote.length <= 2e3;
|
|
770
1509
|
const title = mode === "create" ? `${copy.createTitlePrefix}: ${target?.component_label ?? ""}` : mode === "edit" ? `${copy.editTitlePrefix}: ${target?.component_label ?? ""}` : `${copy.replyTitlePrefix}: ${target?.component_label ?? ""}`;
|
|
1510
|
+
const handleCloseModal = () => {
|
|
1511
|
+
resetVoiceCapture();
|
|
1512
|
+
closeModal();
|
|
1513
|
+
};
|
|
1514
|
+
const handleStartVoiceInput = async () => {
|
|
1515
|
+
await startVoiceInput(client.issueReporting.createVoiceToken);
|
|
1516
|
+
};
|
|
1517
|
+
const handleStopVoiceInput = () => {
|
|
1518
|
+
stopVoiceInput();
|
|
1519
|
+
};
|
|
1520
|
+
const handleAppendTranscript = () => {
|
|
1521
|
+
appendTranscript(setNote);
|
|
1522
|
+
};
|
|
771
1523
|
const handleSubmit = async () => {
|
|
772
1524
|
if (!target || !isValid || isSubmitting) {
|
|
773
1525
|
return;
|
|
774
1526
|
}
|
|
775
1527
|
setSubmitError(null);
|
|
776
1528
|
try {
|
|
777
|
-
const
|
|
1529
|
+
const noteForSubmit = normalizedNote;
|
|
1530
|
+
const effectiveVoiceMetadata = effectiveInputMode === "voice" ? voiceSubmitMetadata ?? {
|
|
1531
|
+
provider: voice.provider,
|
|
1532
|
+
token: "",
|
|
1533
|
+
model_id: voice.modelId,
|
|
1534
|
+
expires_in_seconds: 0,
|
|
1535
|
+
retain_audio: false,
|
|
1536
|
+
attach_transcript: true
|
|
1537
|
+
} : null;
|
|
778
1538
|
if (mode === "create") {
|
|
779
1539
|
await createMutation.mutateAsync({
|
|
780
|
-
target
|
|
781
|
-
|
|
1540
|
+
target: effectiveVoiceMetadata && effectiveInputMode === "voice" ? {
|
|
1541
|
+
...target,
|
|
1542
|
+
metadata: {
|
|
1543
|
+
...target.metadata ?? {},
|
|
1544
|
+
capture: {
|
|
1545
|
+
input_mode: "voice",
|
|
1546
|
+
provider: effectiveVoiceMetadata.provider,
|
|
1547
|
+
model_id: effectiveVoiceMetadata.model_id,
|
|
1548
|
+
retain_audio: effectiveVoiceMetadata.retain_audio,
|
|
1549
|
+
attach_transcript: effectiveVoiceMetadata.attach_transcript
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
} : target,
|
|
1553
|
+
note: noteForSubmit,
|
|
782
1554
|
reporter_role_hint: reporterRoleHint
|
|
783
1555
|
});
|
|
784
1556
|
} else if (mode === "edit" && issue) {
|
|
785
1557
|
await updateMutation.mutateAsync({
|
|
786
1558
|
issueReportId: issue.id,
|
|
787
|
-
note:
|
|
1559
|
+
note: noteForSubmit
|
|
788
1560
|
});
|
|
789
1561
|
} else if (mode === "reply" && issue) {
|
|
790
1562
|
await replyMutation.mutateAsync({
|
|
791
1563
|
issueReportId: issue.id,
|
|
792
|
-
note:
|
|
1564
|
+
note: noteForSubmit,
|
|
793
1565
|
reporterRoleHint
|
|
794
1566
|
});
|
|
795
1567
|
}
|
|
1568
|
+
resetVoiceCapture();
|
|
796
1569
|
closeModal();
|
|
797
1570
|
} catch (submissionError) {
|
|
798
1571
|
setSubmitError(resolveErrorMessage(submissionError, "Failed to submit issue report"));
|
|
799
1572
|
}
|
|
800
1573
|
};
|
|
801
|
-
return /* @__PURE__ */ jsx2(Dialog.Root, { open: isOpen, onOpenChange: (open) => !open &&
|
|
802
|
-
/* @__PURE__ */ jsx2(Dialog.Overlay, { className: "fixed inset-0
|
|
803
|
-
/* @__PURE__ */ jsxs(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
"button",
|
|
818
|
-
{
|
|
819
|
-
type: "button",
|
|
820
|
-
className: "rounded-full border border-rose-300 px-3 py-1 font-medium transition hover:bg-rose-100",
|
|
821
|
-
onClick: () => retryModalHydration(),
|
|
822
|
-
children: copy.retryAction
|
|
823
|
-
}
|
|
824
|
-
),
|
|
825
|
-
/* @__PURE__ */ jsx2(
|
|
826
|
-
"button",
|
|
1574
|
+
return /* @__PURE__ */ jsx2(Dialog.Root, { open: isOpen, onOpenChange: (open) => !open && handleCloseModal(), children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
1575
|
+
/* @__PURE__ */ jsx2(Dialog.Overlay, { className: cn("fixed inset-0 bg-slate-950/45 backdrop-blur-sm", Z_MODAL_OVERLAY) }),
|
|
1576
|
+
/* @__PURE__ */ jsxs(
|
|
1577
|
+
Dialog.Content,
|
|
1578
|
+
{
|
|
1579
|
+
className: cn(
|
|
1580
|
+
"fixed left-1/2 top-1/2 max-w-xl -translate-x-1/2 -translate-y-1/2 border border-slate-200 bg-white p-6 focus:outline-none",
|
|
1581
|
+
Z_MODAL_CONTENT,
|
|
1582
|
+
MODAL_WIDTH,
|
|
1583
|
+
MODAL_RADIUS,
|
|
1584
|
+
MODAL_SHADOW
|
|
1585
|
+
),
|
|
1586
|
+
children: [
|
|
1587
|
+
/* @__PURE__ */ jsx2(Dialog.Title, { className: "text-lg font-semibold text-slate-950", children: title }),
|
|
1588
|
+
/* @__PURE__ */ jsx2(Dialog.Description, { className: "mt-2 text-sm text-slate-600", children: /* @__PURE__ */ jsx2(
|
|
1589
|
+
IssueReportModalDescription,
|
|
827
1590
|
{
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
children: copy.cancelAction
|
|
1591
|
+
copy,
|
|
1592
|
+
mode,
|
|
1593
|
+
target
|
|
832
1594
|
}
|
|
833
|
-
)
|
|
834
|
-
] })
|
|
835
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
836
|
-
mode === "reply" && issue ? /* @__PURE__ */ jsxs("div", { className: "mt-5 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3", children: [
|
|
837
|
-
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium uppercase tracking-wide text-slate-500", children: copy.originalIssueLabel }),
|
|
838
|
-
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm text-slate-700", children: issue.note })
|
|
839
|
-
] }) : null,
|
|
840
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-5 space-y-2", children: [
|
|
1595
|
+
) }),
|
|
841
1596
|
/* @__PURE__ */ jsx2(
|
|
842
|
-
|
|
1597
|
+
IssueReportModalBody,
|
|
843
1598
|
{
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1599
|
+
copy,
|
|
1600
|
+
mode,
|
|
1601
|
+
issue,
|
|
1602
|
+
isHydrating,
|
|
1603
|
+
error,
|
|
1604
|
+
canUseVoice,
|
|
1605
|
+
canUseText,
|
|
1606
|
+
effectiveInputMode,
|
|
1607
|
+
note,
|
|
1608
|
+
normalizedNote,
|
|
1609
|
+
isValid,
|
|
1610
|
+
isSubmitting,
|
|
1611
|
+
isVoiceActive,
|
|
1612
|
+
isVoiceConnecting,
|
|
1613
|
+
voice,
|
|
1614
|
+
voiceTokenResult,
|
|
1615
|
+
committedTranscript,
|
|
1616
|
+
voiceError,
|
|
1617
|
+
scribeError,
|
|
1618
|
+
submitError,
|
|
1619
|
+
onRetryHydration: () => void retryModalHydration(),
|
|
1620
|
+
onClose: handleCloseModal,
|
|
1621
|
+
onSelectText: () => setInputMode("text"),
|
|
1622
|
+
onSelectVoice: () => setInputMode("voice"),
|
|
1623
|
+
onStartVoiceInput: () => void handleStartVoiceInput(),
|
|
1624
|
+
onStopVoiceInput: handleStopVoiceInput,
|
|
1625
|
+
onAppendTranscript: handleAppendTranscript,
|
|
1626
|
+
onNoteChange: setNote,
|
|
1627
|
+
onSubmit: () => void handleSubmit()
|
|
856
1628
|
}
|
|
857
1629
|
),
|
|
858
|
-
/* @__PURE__ */
|
|
859
|
-
/* @__PURE__ */ jsx2("span", { children: getIssueNoteLengthMessage(note, copy) }),
|
|
860
|
-
/* @__PURE__ */ jsx2("span", { children: copy.keyboardShortcutHint })
|
|
861
|
-
] })
|
|
862
|
-
] }),
|
|
863
|
-
submitError ? /* @__PURE__ */ jsx2("div", { className: "mt-4 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700", children: submitError }) : null,
|
|
864
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex justify-end gap-3", children: [
|
|
865
|
-
/* @__PURE__ */ jsx2(
|
|
866
|
-
"button",
|
|
867
|
-
{
|
|
868
|
-
type: "button",
|
|
869
|
-
className: "rounded-full border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50",
|
|
870
|
-
onClick: closeModal,
|
|
871
|
-
disabled: isSubmitting,
|
|
872
|
-
children: copy.cancelAction
|
|
873
|
-
}
|
|
874
|
-
),
|
|
875
|
-
/* @__PURE__ */ jsx2(
|
|
1630
|
+
/* @__PURE__ */ jsx2(Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsx2(
|
|
876
1631
|
"button",
|
|
877
1632
|
{
|
|
878
1633
|
type: "button",
|
|
879
|
-
className:
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
),
|
|
883
|
-
onClick: () => void handleSubmit(),
|
|
884
|
-
disabled: !isValid || isSubmitting,
|
|
885
|
-
children: isSubmitting ? copy.submittingAction : copy.submitAction
|
|
1634
|
+
className: "absolute right-4 top-4 inline-flex h-9 w-9 items-center justify-center rounded-full border border-slate-200 text-slate-500 transition hover:bg-slate-50 hover:text-slate-900",
|
|
1635
|
+
"aria-label": "Close issue report modal",
|
|
1636
|
+
children: /* @__PURE__ */ jsx2(X, { className: "h-4 w-4" })
|
|
886
1637
|
}
|
|
887
|
-
)
|
|
888
|
-
]
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
"button",
|
|
892
|
-
{
|
|
893
|
-
type: "button",
|
|
894
|
-
className: "absolute right-4 top-4 inline-flex h-9 w-9 items-center justify-center rounded-full border border-slate-200 text-slate-500 transition hover:bg-slate-50 hover:text-slate-900",
|
|
895
|
-
"aria-label": "Close issue report modal",
|
|
896
|
-
children: /* @__PURE__ */ jsx2(X, { className: "h-4 w-4" })
|
|
897
|
-
}
|
|
898
|
-
) })
|
|
899
|
-
] })
|
|
1638
|
+
) })
|
|
1639
|
+
]
|
|
1640
|
+
}
|
|
1641
|
+
)
|
|
900
1642
|
] }) });
|
|
901
1643
|
}
|
|
902
1644
|
function FloatingIssueReportButton({
|
|
@@ -918,7 +1660,7 @@ function FloatingIssueReportButton({
|
|
|
918
1660
|
}
|
|
919
1661
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
920
1662
|
/* @__PURE__ */ jsx2(IssueReportModeBanner, {}),
|
|
921
|
-
!isReportMode ? /* @__PURE__ */ jsx2("div", { className: cn("fixed bottom-12 right-4
|
|
1663
|
+
!isReportMode ? /* @__PURE__ */ jsx2("div", { className: cn("fixed bottom-12 right-4", Z_FLOATING_BUTTON, positionClassName), children: /* @__PURE__ */ jsx2(IssueReportPopover, { children: /* @__PURE__ */ jsx2(
|
|
922
1664
|
"button",
|
|
923
1665
|
{
|
|
924
1666
|
type: "button",
|
|
@@ -949,9 +1691,23 @@ function ReportableSection({
|
|
|
949
1691
|
}) {
|
|
950
1692
|
const reportMode = useReportMode();
|
|
951
1693
|
const isSelectable = Boolean(reportMode?.isReportMode);
|
|
952
|
-
|
|
1694
|
+
const targetId = React2.useRef(/* @__PURE__ */ Symbol("reportable-section"));
|
|
1695
|
+
const elementRef = React2.useRef(null);
|
|
1696
|
+
useEffect2(() => {
|
|
1697
|
+
if (!reportMode) {
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
reportMode.registerTarget(targetId.current, reportableName, () => elementRef.current);
|
|
1701
|
+
return () => {
|
|
1702
|
+
reportMode.unregisterTarget(targetId.current);
|
|
1703
|
+
};
|
|
1704
|
+
}, [reportMode, reportableName]);
|
|
1705
|
+
return React2.createElement(
|
|
953
1706
|
Component,
|
|
954
1707
|
{
|
|
1708
|
+
ref: (node) => {
|
|
1709
|
+
elementRef.current = node;
|
|
1710
|
+
},
|
|
955
1711
|
className: cn(
|
|
956
1712
|
className,
|
|
957
1713
|
isSelectable && "cursor-pointer ring-2 ring-amber-400 transition hover:ring-amber-500"
|
|
@@ -966,13 +1722,14 @@ function ReportableSection({
|
|
|
966
1722
|
}
|
|
967
1723
|
} : void 0,
|
|
968
1724
|
role: isSelectable ? "button" : void 0,
|
|
969
|
-
tabIndex: isSelectable ? 0 : void 0
|
|
970
|
-
|
|
971
|
-
|
|
1725
|
+
tabIndex: isSelectable ? 0 : void 0
|
|
1726
|
+
},
|
|
1727
|
+
children
|
|
972
1728
|
);
|
|
973
1729
|
}
|
|
974
1730
|
export {
|
|
975
1731
|
FloatingIssueReportButton,
|
|
1732
|
+
IssueReportingPageConfig,
|
|
976
1733
|
IssueReportingProvider,
|
|
977
1734
|
ReportModeContext,
|
|
978
1735
|
ReportableSection,
|