@arbidocs/blocks 0.5.112 → 0.5.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConnectedCallDock-KUF6RX76.cjs → ConnectedCallDock-2KXEQENS.cjs} +26 -26
- package/dist/{ConnectedCallDock-KUF6RX76.cjs.map → ConnectedCallDock-2KXEQENS.cjs.map} +1 -1
- package/dist/{ConnectedCallRoom-ZKIYB2JG.cjs → ConnectedCallRoom-MU3NPNXF.cjs} +18 -18
- package/dist/{ConnectedCallRoom-ZKIYB2JG.cjs.map → ConnectedCallRoom-MU3NPNXF.cjs.map} +1 -1
- package/dist/{ConnectedMailInviteCard-P65MQM6J.cjs → ConnectedMailInviteCard-EINB3THA.cjs} +5 -5
- package/dist/{ConnectedMailInviteCard-P65MQM6J.cjs.map → ConnectedMailInviteCard-EINB3THA.cjs.map} +1 -1
- package/dist/{ConnectedNativeDocxComparison-S4RKDH2A.cjs → ConnectedNativeDocxComparison-VH3I65H6.cjs} +21 -21
- package/dist/{ConnectedNativeDocxComparison-S4RKDH2A.cjs.map → ConnectedNativeDocxComparison-VH3I65H6.cjs.map} +1 -1
- package/dist/{ConnectedProductionSetDialog-HX4HTQ3Z.cjs → ConnectedProductionSetDialog-4EAY6KAZ.cjs} +8 -8
- package/dist/{ConnectedProductionSetDialog-HX4HTQ3Z.cjs.map → ConnectedProductionSetDialog-4EAY6KAZ.cjs.map} +1 -1
- package/dist/{ConnectedStreamedWordArtifact-A2A7Z3TT.cjs → ConnectedStreamedWordArtifact-KQSHCA75.cjs} +21 -21
- package/dist/{ConnectedStreamedWordArtifact-A2A7Z3TT.cjs.map → ConnectedStreamedWordArtifact-KQSHCA75.cjs.map} +1 -1
- package/dist/{DocumentReviewCard-4A7NF474.cjs → DocumentReviewCard-OCPFVMLD.cjs} +22 -22
- package/dist/{DocumentReviewCard-4A7NF474.cjs.map → DocumentReviewCard-OCPFVMLD.cjs.map} +1 -1
- package/dist/{FilesViewerDialog-PKCBZXDG.cjs → FilesViewerDialog-BAGOEMXW.cjs} +54 -54
- package/dist/{FilesViewerDialog-PKCBZXDG.cjs.map → FilesViewerDialog-BAGOEMXW.cjs.map} +1 -1
- package/dist/{WorkspaceTableView-J3C6VH4T.cjs → WorkspaceTableView-IMAZICIZ.cjs} +9 -9
- package/dist/{WorkspaceTableView-J3C6VH4T.cjs.map → WorkspaceTableView-IMAZICIZ.cjs.map} +1 -1
- package/dist/agents/index.cjs +298 -298
- package/dist/{ai-OYTFPMXI.cjs → ai-7U4B4CVF.cjs} +27 -27
- package/dist/ai-7U4B4CVF.cjs.map +1 -0
- package/dist/calls/index.cjs +147 -147
- package/dist/chat/index.cjs +29 -29
- package/dist/{chunk-37JPFCCU.cjs → chunk-2W5E62EY.cjs} +6 -6
- package/dist/{chunk-37JPFCCU.cjs.map → chunk-2W5E62EY.cjs.map} +1 -1
- package/dist/{chunk-N6AZYEYL.cjs → chunk-3ODEWSRO.cjs} +4 -4
- package/dist/{chunk-N6AZYEYL.cjs.map → chunk-3ODEWSRO.cjs.map} +1 -1
- package/dist/{chunk-VDFYMVMP.cjs → chunk-3R6YHJ3G.cjs} +4 -4
- package/dist/{chunk-VDFYMVMP.cjs.map → chunk-3R6YHJ3G.cjs.map} +1 -1
- package/dist/{chunk-5RCCZYAE.cjs → chunk-3U36VYMD.cjs} +36 -36
- package/dist/{chunk-5RCCZYAE.cjs.map → chunk-3U36VYMD.cjs.map} +1 -1
- package/dist/{chunk-A4FGU6YC.cjs → chunk-436I7PQL.cjs} +7 -7
- package/dist/{chunk-A4FGU6YC.cjs.map → chunk-436I7PQL.cjs.map} +1 -1
- package/dist/{chunk-J2RXQVVF.cjs → chunk-4EK7TAP7.cjs} +5 -5
- package/dist/{chunk-J2RXQVVF.cjs.map → chunk-4EK7TAP7.cjs.map} +1 -1
- package/dist/{chunk-TP5PGWIO.cjs → chunk-4IOYQLKG.cjs} +3 -3
- package/dist/{chunk-TP5PGWIO.cjs.map → chunk-4IOYQLKG.cjs.map} +1 -1
- package/dist/{chunk-ZQLRWA3Y.cjs → chunk-4NPCDS2X.cjs} +4 -4
- package/dist/{chunk-ZQLRWA3Y.cjs.map → chunk-4NPCDS2X.cjs.map} +1 -1
- package/dist/{chunk-ZEQEIFZJ.cjs → chunk-57EE6CMW.cjs} +24 -24
- package/dist/{chunk-ZEQEIFZJ.cjs.map → chunk-57EE6CMW.cjs.map} +1 -1
- package/dist/{chunk-NMT4UZQM.cjs → chunk-57NHXEU2.cjs} +5 -5
- package/dist/{chunk-NMT4UZQM.cjs.map → chunk-57NHXEU2.cjs.map} +1 -1
- package/dist/{chunk-4QTQS4LS.cjs → chunk-5QUL5BZO.cjs} +5 -5
- package/dist/{chunk-4QTQS4LS.cjs.map → chunk-5QUL5BZO.cjs.map} +1 -1
- package/dist/{chunk-MUA5PJGX.cjs → chunk-5SKHK3BG.cjs} +4 -4
- package/dist/{chunk-MUA5PJGX.cjs.map → chunk-5SKHK3BG.cjs.map} +1 -1
- package/dist/{chunk-AFXDLDPU.cjs → chunk-5UYFLFNR.cjs} +4 -4
- package/dist/{chunk-AFXDLDPU.cjs.map → chunk-5UYFLFNR.cjs.map} +1 -1
- package/dist/{chunk-FJXJ43JO.cjs → chunk-6DCIZA3X.cjs} +101 -101
- package/dist/{chunk-FJXJ43JO.cjs.map → chunk-6DCIZA3X.cjs.map} +1 -1
- package/dist/{chunk-G5TRBLKF.cjs → chunk-7TEUGFIL.cjs} +4 -4
- package/dist/{chunk-G5TRBLKF.cjs.map → chunk-7TEUGFIL.cjs.map} +1 -1
- package/dist/{chunk-ZNZGYSVB.cjs → chunk-7X3FS5HQ.cjs} +39 -39
- package/dist/{chunk-ZNZGYSVB.cjs.map → chunk-7X3FS5HQ.cjs.map} +1 -1
- package/dist/{chunk-DZDARC3V.cjs → chunk-7XT5B2DZ.cjs} +70 -70
- package/dist/{chunk-DZDARC3V.cjs.map → chunk-7XT5B2DZ.cjs.map} +1 -1
- package/dist/{chunk-5UMKMUB5.cjs → chunk-AHT6DQNI.cjs} +68 -68
- package/dist/{chunk-5UMKMUB5.cjs.map → chunk-AHT6DQNI.cjs.map} +1 -1
- package/dist/{chunk-SRET7LYT.cjs → chunk-BW5MF464.cjs} +4 -4
- package/dist/{chunk-SRET7LYT.cjs.map → chunk-BW5MF464.cjs.map} +1 -1
- package/dist/{chunk-NP4EWPIP.cjs → chunk-CJYWHUZR.cjs} +6 -6
- package/dist/{chunk-NP4EWPIP.cjs.map → chunk-CJYWHUZR.cjs.map} +1 -1
- package/dist/{chunk-NTD7VSHO.cjs → chunk-CMGNRMPA.cjs} +4 -4
- package/dist/{chunk-NTD7VSHO.cjs.map → chunk-CMGNRMPA.cjs.map} +1 -1
- package/dist/{chunk-Z3VNKPPH.cjs → chunk-E3Y7RI3J.cjs} +4 -4
- package/dist/{chunk-Z3VNKPPH.cjs.map → chunk-E3Y7RI3J.cjs.map} +1 -1
- package/dist/{chunk-TDO5UWXF.cjs → chunk-E4BLRF2L.cjs} +5 -5
- package/dist/{chunk-TDO5UWXF.cjs.map → chunk-E4BLRF2L.cjs.map} +1 -1
- package/dist/{chunk-WT7CCCIN.cjs → chunk-GNX3FBBX.cjs} +4 -4
- package/dist/{chunk-WT7CCCIN.cjs.map → chunk-GNX3FBBX.cjs.map} +1 -1
- package/dist/{chunk-3NEI27QQ.cjs → chunk-GQXPNIRE.cjs} +14 -14
- package/dist/{chunk-3NEI27QQ.cjs.map → chunk-GQXPNIRE.cjs.map} +1 -1
- package/dist/{chunk-7OPOPYV6.cjs → chunk-H5OLHP2U.cjs} +3 -3
- package/dist/{chunk-7OPOPYV6.cjs.map → chunk-H5OLHP2U.cjs.map} +1 -1
- package/dist/{chunk-IIWJQEBC.cjs → chunk-H7PHOTX2.cjs} +10 -10
- package/dist/{chunk-IIWJQEBC.cjs.map → chunk-H7PHOTX2.cjs.map} +1 -1
- package/dist/{chunk-LQHXUSWZ.cjs → chunk-HQDR43JC.cjs} +6 -6
- package/dist/{chunk-LQHXUSWZ.cjs.map → chunk-HQDR43JC.cjs.map} +1 -1
- package/dist/{chunk-6LQN4BMX.cjs → chunk-IDCD5TEZ.cjs} +5 -5
- package/dist/{chunk-6LQN4BMX.cjs.map → chunk-IDCD5TEZ.cjs.map} +1 -1
- package/dist/{chunk-6YOZKXV6.cjs → chunk-JR2YSBD5.cjs} +4 -4
- package/dist/{chunk-6YOZKXV6.cjs.map → chunk-JR2YSBD5.cjs.map} +1 -1
- package/dist/{chunk-3GYOWDNM.cjs → chunk-JSQQV23D.cjs} +5 -5
- package/dist/{chunk-3GYOWDNM.cjs.map → chunk-JSQQV23D.cjs.map} +1 -1
- package/dist/{chunk-ZFKWCGA5.cjs → chunk-JV5TVUOU.cjs} +4 -4
- package/dist/{chunk-ZFKWCGA5.cjs.map → chunk-JV5TVUOU.cjs.map} +1 -1
- package/dist/{chunk-TXFZAA3R.cjs → chunk-LFRNBT2I.cjs} +23 -23
- package/dist/{chunk-TXFZAA3R.cjs.map → chunk-LFRNBT2I.cjs.map} +1 -1
- package/dist/{chunk-R7CJEIA5.cjs → chunk-LYTIQQQ2.cjs} +3 -3
- package/dist/{chunk-R7CJEIA5.cjs.map → chunk-LYTIQQQ2.cjs.map} +1 -1
- package/dist/{chunk-6UYTG2YG.cjs → chunk-MC4F2URI.cjs} +28 -28
- package/dist/{chunk-6UYTG2YG.cjs.map → chunk-MC4F2URI.cjs.map} +1 -1
- package/dist/{chunk-NNG34ONS.cjs → chunk-MGQSLMTC.cjs} +78 -78
- package/dist/{chunk-NNG34ONS.cjs.map → chunk-MGQSLMTC.cjs.map} +1 -1
- package/dist/{chunk-3Q5L7XVI.cjs → chunk-MZ2KZXNU.cjs} +9 -9
- package/dist/{chunk-3Q5L7XVI.cjs.map → chunk-MZ2KZXNU.cjs.map} +1 -1
- package/dist/{chunk-PJ762EO2.cjs → chunk-NHHEMBXQ.cjs} +24 -24
- package/dist/{chunk-PJ762EO2.cjs.map → chunk-NHHEMBXQ.cjs.map} +1 -1
- package/dist/{chunk-ENUZ35NR.cjs → chunk-P5AQYC4D.cjs} +4 -4
- package/dist/{chunk-ENUZ35NR.cjs.map → chunk-P5AQYC4D.cjs.map} +1 -1
- package/dist/{chunk-5I2VUNZR.cjs → chunk-PGJYD67F.cjs} +6 -6
- package/dist/{chunk-5I2VUNZR.cjs.map → chunk-PGJYD67F.cjs.map} +1 -1
- package/dist/{chunk-BYN7UMC5.cjs → chunk-PL7HUTM3.cjs} +8 -8
- package/dist/{chunk-BYN7UMC5.cjs.map → chunk-PL7HUTM3.cjs.map} +1 -1
- package/dist/{chunk-NOYQ23I7.cjs → chunk-Q6DQGGP4.cjs} +6 -6
- package/dist/{chunk-NOYQ23I7.cjs.map → chunk-Q6DQGGP4.cjs.map} +1 -1
- package/dist/{chunk-T7J352JD.cjs → chunk-QMIHLJMH.cjs} +6 -6
- package/dist/{chunk-T7J352JD.cjs.map → chunk-QMIHLJMH.cjs.map} +1 -1
- package/dist/{chunk-LTEXVRCH.cjs → chunk-SBWIA4PL.cjs} +21 -21
- package/dist/{chunk-LTEXVRCH.cjs.map → chunk-SBWIA4PL.cjs.map} +1 -1
- package/dist/{chunk-ZH7LDZJX.cjs → chunk-TDN33HSK.cjs} +14 -14
- package/dist/{chunk-ZH7LDZJX.cjs.map → chunk-TDN33HSK.cjs.map} +1 -1
- package/dist/{chunk-U5T46QCS.cjs → chunk-TKHCIHE2.cjs} +3 -3
- package/dist/chunk-TKHCIHE2.cjs.map +1 -0
- package/dist/{chunk-WIAWHETB.cjs → chunk-ULPTSXXO.cjs} +4 -4
- package/dist/{chunk-WIAWHETB.cjs.map → chunk-ULPTSXXO.cjs.map} +1 -1
- package/dist/{chunk-GTWEWDIZ.cjs → chunk-UNDQYTRF.cjs} +8 -8
- package/dist/{chunk-GTWEWDIZ.cjs.map → chunk-UNDQYTRF.cjs.map} +1 -1
- package/dist/{chunk-OKHKJ5ML.cjs → chunk-UUENH4BJ.cjs} +11 -11
- package/dist/{chunk-OKHKJ5ML.cjs.map → chunk-UUENH4BJ.cjs.map} +1 -1
- package/dist/{chunk-K5MYEWD5.cjs → chunk-UZHWXKS6.cjs} +7 -7
- package/dist/{chunk-K5MYEWD5.cjs.map → chunk-UZHWXKS6.cjs.map} +1 -1
- package/dist/{chunk-QVHP4R7T.cjs → chunk-V5IMYLYK.cjs} +29 -29
- package/dist/{chunk-QVHP4R7T.cjs.map → chunk-V5IMYLYK.cjs.map} +1 -1
- package/dist/{chunk-XW524KUX.cjs → chunk-VJCSEMXT.cjs} +4 -4
- package/dist/{chunk-XW524KUX.cjs.map → chunk-VJCSEMXT.cjs.map} +1 -1
- package/dist/{chunk-B5442TKX.cjs → chunk-VRK3IQAP.cjs} +4 -4
- package/dist/{chunk-B5442TKX.cjs.map → chunk-VRK3IQAP.cjs.map} +1 -1
- package/dist/{chunk-MYRKL3JO.cjs → chunk-VWTOIGX4.cjs} +4 -4
- package/dist/{chunk-MYRKL3JO.cjs.map → chunk-VWTOIGX4.cjs.map} +1 -1
- package/dist/{chunk-Q7DB5S7N.cjs → chunk-WW6LXQY6.cjs} +12 -12
- package/dist/{chunk-Q7DB5S7N.cjs.map → chunk-WW6LXQY6.cjs.map} +1 -1
- package/dist/{chunk-NJCK6V6U.cjs → chunk-XKDUPU7B.cjs} +4 -4
- package/dist/{chunk-NJCK6V6U.cjs.map → chunk-XKDUPU7B.cjs.map} +1 -1
- package/dist/{chunk-YAPXIANR.cjs → chunk-YVCOQHCE.cjs} +5 -5
- package/dist/{chunk-YAPXIANR.cjs.map → chunk-YVCOQHCE.cjs.map} +1 -1
- package/dist/{chunk-TOM2P3MA.cjs → chunk-ZZA756I4.cjs} +4 -4
- package/dist/{chunk-TOM2P3MA.cjs.map → chunk-ZZA756I4.cjs.map} +1 -1
- package/dist/citations/index.cjs +8 -8
- package/dist/connect/docx.cjs +24 -24
- package/dist/connect/index.cjs +1489 -1433
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/viewer.cjs +40 -40
- package/dist/connection/ConnectedWorkflowBuilder.d.ts +11 -0
- package/dist/connection/ConnectedWorkflowBuilder.js +42 -3
- package/dist/connection/ConnectedWorkflowBuilder.js.map +2 -2
- package/dist/connection/conversation/ConnectedConversationPage.js +36 -2
- package/dist/connection/conversation/ConnectedConversationPage.js.map +2 -2
- package/dist/connection/report-problem/ReportProblemDialog.d.ts +2 -2
- package/dist/connection/report-problem/ReportProblemDialog.js +51 -19
- package/dist/connection/report-problem/ReportProblemDialog.js.map +2 -2
- package/dist/connection/workflows/ConnectedWorkflowsPage.js +10 -3
- package/dist/connection/workflows/ConnectedWorkflowsPage.js.map +2 -2
- package/dist/connection/workflows/useWorkflowsPageState.d.ts +1 -0
- package/dist/connection/workflows/useWorkflowsPageState.js.map +2 -2
- package/dist/dashboards/index.cjs +23 -23
- package/dist/embed/index.cjs +49 -49
- package/dist/facts/index.cjs +45 -45
- package/dist/files/index.cjs +225 -225
- package/dist/home/index.cjs +32 -32
- package/dist/index.cjs +1750 -1750
- package/dist/provider/environment.d.ts +5 -0
- package/dist/provider/environment.js +1 -1
- package/dist/provider/environment.js.map +2 -2
- package/dist/provider/requests.d.ts +2 -0
- package/dist/provider/requests.js.map +2 -2
- package/dist/retrieval/index.cjs +4 -4
- package/dist/settings/index.cjs +148 -148
- package/dist/shell/index.cjs +91 -91
- package/dist/site/index.cjs +5 -5
- package/dist/studio/index.cjs +43 -43
- package/dist/workflow-canvas/index.cjs +114 -72
- package/dist/workflow-canvas/index.cjs.map +1 -1
- package/dist/workspaces/index.cjs +18 -18
- package/node_modules/@arbidocs/coedit/package.json +1 -1
- package/package.json +5 -5
- package/dist/ai-OYTFPMXI.cjs.map +0 -1
- package/dist/chunk-U5T46QCS.cjs.map +0 -1
|
@@ -37,6 +37,7 @@ function ReportProblemDialog({
|
|
|
37
37
|
const [natural, setNatural] = useState(null);
|
|
38
38
|
const [frameSize, setFrameSize] = useState(null);
|
|
39
39
|
const [cropArea, setCropArea] = useState(null);
|
|
40
|
+
const [cropping, setCropping] = useState(false);
|
|
40
41
|
const [busy, setBusy] = useState(false);
|
|
41
42
|
const [failure, setFailure] = useState(null);
|
|
42
43
|
const [ticketReference, setTicketReference] = useState(null);
|
|
@@ -44,28 +45,33 @@ function ReportProblemDialog({
|
|
|
44
45
|
const [submissionId] = useState(() => crypto.randomUUID());
|
|
45
46
|
const imgRef = useRef(null);
|
|
46
47
|
const dragStart = useRef(null);
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const screenshot = image ?? frame;
|
|
49
|
+
const imageUrl = useMemo(
|
|
50
|
+
() => screenshot ? URL.createObjectURL(screenshot) : null,
|
|
51
|
+
[screenshot]
|
|
52
|
+
);
|
|
50
53
|
useEffect(() => () => void (imageUrl && URL.revokeObjectURL(imageUrl)), [imageUrl]);
|
|
51
54
|
useEffect(() => {
|
|
52
55
|
setFrameSize(null);
|
|
53
|
-
if (!
|
|
56
|
+
if (!imageUrl) return;
|
|
54
57
|
const img = new Image();
|
|
55
58
|
img.onload = () => setFrameSize({ width: img.naturalWidth, height: img.naturalHeight });
|
|
56
|
-
img.src =
|
|
57
|
-
}, [
|
|
59
|
+
img.src = imageUrl;
|
|
60
|
+
}, [imageUrl]);
|
|
58
61
|
useEffect(() => {
|
|
59
62
|
setMarks([]);
|
|
60
63
|
setDraft(null);
|
|
61
64
|
setNatural(null);
|
|
62
|
-
|
|
65
|
+
setCropping(false);
|
|
66
|
+
setCropArea(null);
|
|
67
|
+
}, [screenshot]);
|
|
63
68
|
const applyCrop = async () => {
|
|
64
|
-
if (!
|
|
69
|
+
if (!imageUrl || !cropArea) return;
|
|
65
70
|
setBusy(true);
|
|
66
71
|
try {
|
|
67
|
-
const { blob } = await cropImage(
|
|
72
|
+
const { blob } = await cropImage(imageUrl, cropArea, { mimeType: "image/png" });
|
|
68
73
|
onCropped(blob);
|
|
74
|
+
setCropping(false);
|
|
69
75
|
} catch {
|
|
70
76
|
notify("Couldn't crop the screenshot. Please try again.", "error");
|
|
71
77
|
} finally {
|
|
@@ -82,8 +88,8 @@ function ReportProblemDialog({
|
|
|
82
88
|
);
|
|
83
89
|
};
|
|
84
90
|
const renderImage = async () => {
|
|
85
|
-
if (!
|
|
86
|
-
const bitmap = await createImageBitmap(
|
|
91
|
+
if (!screenshot || !natural) return null;
|
|
92
|
+
const bitmap = await createImageBitmap(screenshot);
|
|
87
93
|
try {
|
|
88
94
|
return await renderAnnotatedScreenshot(bitmap, marks, { happened: description, expected: "" });
|
|
89
95
|
} finally {
|
|
@@ -95,8 +101,8 @@ function ReportProblemDialog({
|
|
|
95
101
|
setBusy(true);
|
|
96
102
|
setFailure(null);
|
|
97
103
|
try {
|
|
98
|
-
const rendered =
|
|
99
|
-
if (
|
|
104
|
+
const rendered = screenshot ? await renderImage() : null;
|
|
105
|
+
if (screenshot && !rendered) throw new Error("The screenshot is not ready. Please try again.");
|
|
100
106
|
const receipt = await onSubmit({
|
|
101
107
|
description: description.trim(),
|
|
102
108
|
image: rendered ?? void 0,
|
|
@@ -121,16 +127,15 @@ function ReportProblemDialog({
|
|
|
121
127
|
width: `${mark.w / natural.width * 100}%`,
|
|
122
128
|
height: `${mark.h / natural.height * 100}%`
|
|
123
129
|
} : void 0;
|
|
124
|
-
const cropping = !!frameUrl;
|
|
125
130
|
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => !next && !busy && onClose(), children: /* @__PURE__ */ jsxs(
|
|
126
131
|
DialogContent,
|
|
127
132
|
{
|
|
128
|
-
className: `flex max-h-[92vh] w-[96vw] flex-col gap-3 p-4 ${ticketId === null &&
|
|
133
|
+
className: `flex max-h-[92vh] w-[96vw] flex-col gap-3 p-4 ${ticketId === null && screenshot ? "max-w-[96vw] sm:max-w-[min(96vw,64rem)]" : "max-w-lg sm:max-w-lg"}`,
|
|
129
134
|
"data-testid": "report-problem-dialog",
|
|
130
135
|
children: [
|
|
131
136
|
/* @__PURE__ */ jsxs("div", { className: "pr-8", children: [
|
|
132
137
|
/* @__PURE__ */ jsx(DialogTitle, { children: ticketId === null ? "Contact support" : "Support request sent" }),
|
|
133
|
-
/* @__PURE__ */ jsx(DialogDescription, { children: ticketId !== null ? "We have received your request. Track updates in My requests." : cropping ? "Crop the screenshot to the part that shows the problem. Only the cropped part is kept." :
|
|
138
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: ticketId !== null ? "We have received your request. Track updates in My requests." : cropping ? "Crop the screenshot to the part that shows the problem. Only the cropped part is kept." : screenshot ? "Black out anything confidential before sending. Blacked-out areas are removed from the image." : "Tell us what you need help with. You can choose to add a screenshot." })
|
|
134
139
|
] }),
|
|
135
140
|
ticketId !== null && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 rounded-md border border-border bg-muted p-4", children: [
|
|
136
141
|
/* @__PURE__ */ jsxs("p", { className: "font-medium", "data-testid": "report-ticket-receipt", children: [
|
|
@@ -139,11 +144,11 @@ function ReportProblemDialog({
|
|
|
139
144
|
] }),
|
|
140
145
|
/* @__PURE__ */ jsx(Button, { onClick: onClose, "data-testid": "report-receipt-close", children: "Close" })
|
|
141
146
|
] }),
|
|
142
|
-
cropping ? /* @__PURE__ */ jsxs("div", { className: "grid gap-2", hidden: ticketId !== null, children: [
|
|
147
|
+
cropping && imageUrl ? /* @__PURE__ */ jsxs("div", { className: "grid gap-2", hidden: ticketId !== null, children: [
|
|
143
148
|
/* @__PURE__ */ jsx(
|
|
144
149
|
ImageCropper,
|
|
145
150
|
{
|
|
146
|
-
image:
|
|
151
|
+
image: imageUrl,
|
|
147
152
|
aspect: frameSize ? frameSize.width / frameSize.height : window.innerWidth / window.innerHeight,
|
|
148
153
|
maxZoom: 6,
|
|
149
154
|
cropAreaClassName: "h-[50vh] min-h-64",
|
|
@@ -152,6 +157,15 @@ function ReportProblemDialog({
|
|
|
152
157
|
frameSize ? "sized" : "unsized"
|
|
153
158
|
),
|
|
154
159
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
|
|
160
|
+
/* @__PURE__ */ jsx(
|
|
161
|
+
Button,
|
|
162
|
+
{
|
|
163
|
+
variant: "ghost",
|
|
164
|
+
onClick: () => setCropping(false),
|
|
165
|
+
"data-testid": "report-cancel-crop",
|
|
166
|
+
children: "Cancel crop"
|
|
167
|
+
}
|
|
168
|
+
),
|
|
155
169
|
/* @__PURE__ */ jsxs(
|
|
156
170
|
Button,
|
|
157
171
|
{
|
|
@@ -240,6 +254,24 @@ function ReportProblemDialog({
|
|
|
240
254
|
/* @__PURE__ */ jsx(EyeOff, { className: "size-4" }),
|
|
241
255
|
" Drag over anything confidential to black it out."
|
|
242
256
|
] }),
|
|
257
|
+
/* @__PURE__ */ jsxs(
|
|
258
|
+
Button,
|
|
259
|
+
{
|
|
260
|
+
size: "sm",
|
|
261
|
+
variant: "ghost",
|
|
262
|
+
disabled: !!marks.length,
|
|
263
|
+
title: marks.length ? "Undo blacked-out areas before cropping" : void 0,
|
|
264
|
+
onClick: () => {
|
|
265
|
+
setCropArea(null);
|
|
266
|
+
setCropping(true);
|
|
267
|
+
},
|
|
268
|
+
"data-testid": "report-crop",
|
|
269
|
+
children: [
|
|
270
|
+
/* @__PURE__ */ jsx(Crop, { className: "mr-1 size-4" }),
|
|
271
|
+
" Crop"
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
),
|
|
243
275
|
/* @__PURE__ */ jsxs(
|
|
244
276
|
Button,
|
|
245
277
|
{
|
|
@@ -322,7 +354,7 @@ function ReportProblemDialog({
|
|
|
322
354
|
/* @__PURE__ */ jsx(
|
|
323
355
|
Button,
|
|
324
356
|
{
|
|
325
|
-
disabled: !onSubmit || !description.trim() || busy || cropping || !!
|
|
357
|
+
disabled: !onSubmit || !description.trim() || busy || cropping || !!screenshot && !natural,
|
|
326
358
|
onClick: () => void submit(),
|
|
327
359
|
"data-testid": "report-send",
|
|
328
360
|
children: busy ? "Sending\u2026" : "Send to support"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/report-problem/ReportProblemDialog.tsx"],
|
|
4
|
-
"sourcesContent": ["/** Support form with optional, explicitly chosen and redacted screenshot evidence. */\nimport React, { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport { Camera, Crop, EyeOff, ImageUp, Trash2, Undo2 } from 'lucide-react'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogTitle,\n Textarea,\n} from '@arbidocs/react/ui'\nimport { ImageCropper, cropImage, type CropArea } from '@arbidocs/react'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { canCaptureScreen } from './screen-capture'\nimport {\n isMeaningfulRect,\n rectFromDrag,\n renderAnnotatedScreenshot,\n toImagePoint,\n type Mark,\n} from './screenshot-annotations'\n\nexport interface ReportProblemOptions {\n /** @deprecated No image download is created. Retained for source compatibility. */\n fileNamePrefix?: string\n onSubmit?: (report: {\n description: string\n image?: Blob\n idempotencyKey: string\n }) => Promise<{ ticket_id: number; reference?: string }>\n /** A line beside the actions: where reports go (the site's own words). Default: none. */\n footnote?: ReactNode\n}\n\nexport interface ReportProblemDialogProps extends ReportProblemOptions {\n open: boolean\n /** The full captured frame, to crop. Dropped once a crop is chosen. */\n frame: Blob | null\n /** The kept screenshot (the crop, or a picked file). */\n image: Blob | null\n onCropped: (crop: Blob) => void\n onImageChange: (image: Blob | null) => void\n onRetake: () => void\n onClose: () => void\n}\n\nexport function ReportProblemDialog({\n open,\n frame,\n image,\n onCropped,\n onImageChange,\n onRetake,\n onClose,\n onSubmit,\n footnote,\n}: ReportProblemDialogProps): React.ReactElement {\n const { notify } = useBlocksEnvironment()\n const [description, setDescription] = useState('')\n const [marks, setMarks] = useState<Mark[]>([])\n const [draft, setDraft] = useState<Mark | null>(null)\n const [natural, setNatural] = useState<{ width: number; height: number } | null>(null)\n const [frameSize, setFrameSize] = useState<{ width: number; height: number } | null>(null)\n const [cropArea, setCropArea] = useState<CropArea | null>(null)\n const [busy, setBusy] = useState(false)\n const [failure, setFailure] = useState<string | null>(null)\n const [ticketReference, setTicketReference] = useState<string | null>(null)\n const [ticketId, setTicketId] = useState<number | null>(null)\n const [submissionId] = useState(() => crypto.randomUUID())\n const imgRef = useRef<HTMLImageElement>(null)\n const dragStart = useRef<{ x: number; y: number } | null>(null)\n\n const frameUrl = useMemo(() => (frame ? URL.createObjectURL(frame) : null), [frame])\n useEffect(() => () => void (frameUrl && URL.revokeObjectURL(frameUrl)), [frameUrl])\n const imageUrl = useMemo(() => (image ? URL.createObjectURL(image) : null), [image])\n useEffect(() => () => void (imageUrl && URL.revokeObjectURL(imageUrl)), [imageUrl])\n\n // The frame's size picks the \"Screen\" crop shape.\n useEffect(() => {\n setFrameSize(null)\n if (!frameUrl) return\n const img = new Image()\n img.onload = () => setFrameSize({ width: img.naturalWidth, height: img.naturalHeight })\n img.src = frameUrl\n }, [frameUrl])\n\n // A new screenshot starts new marks; closing starts a new report.\n useEffect(() => {\n setMarks([])\n setDraft(null)\n setNatural(null)\n }, [image])\n\n const applyCrop = async () => {\n if (!frameUrl || !cropArea) return\n setBusy(true)\n try {\n const { blob } = await cropImage(frameUrl, cropArea, { mimeType: 'image/png' })\n onCropped(blob)\n } catch {\n notify(\"Couldn't crop the screenshot. Please try again.\", 'error')\n } finally {\n setBusy(false)\n }\n }\n\n const pointFrom = (event: React.PointerEvent) => {\n const img = imgRef.current\n if (!img || !natural) return null\n return toImagePoint(\n { x: event.clientX, y: event.clientY },\n img.getBoundingClientRect(),\n natural\n )\n }\n\n const renderImage = async (): Promise<Blob | null> => {\n if (!image || !natural) return null\n const bitmap = await createImageBitmap(image)\n try {\n return await renderAnnotatedScreenshot(bitmap, marks, { happened: description, expected: '' })\n } finally {\n bitmap.close()\n }\n }\n\n const submit = async () => {\n if (!onSubmit || !description.trim()) return\n setBusy(true)\n setFailure(null)\n try {\n const rendered = image ? await renderImage() : null\n if (image && !rendered) throw new Error('The screenshot is not ready. Please try again.')\n const receipt = await onSubmit({\n description: description.trim(),\n image: rendered ?? undefined,\n idempotencyKey: submissionId,\n })\n setTicketId(receipt.ticket_id)\n setTicketReference(\n receipt.reference &&\n /^[a-z0-9-]+-[0-9]+$/.test(receipt.reference) &&\n receipt.reference.endsWith(`-${receipt.ticket_id}`)\n ? receipt.reference\n : null\n )\n } catch (error) {\n setFailure(\n error instanceof Error ? error.message : 'Could not send your request. Please try again.'\n )\n } finally {\n setBusy(false)\n }\n }\n\n const shown = draft ? [...marks, draft] : marks\n const percent = (mark: Mark) =>\n natural\n ? {\n left: `${(mark.x / natural.width) * 100}%`,\n top: `${(mark.y / natural.height) * 100}%`,\n width: `${(mark.w / natural.width) * 100}%`,\n height: `${(mark.h / natural.height) * 100}%`,\n }\n : undefined\n const cropping = !!frameUrl\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && !busy && onClose()}>\n <DialogContent\n className={`flex max-h-[92vh] w-[96vw] flex-col gap-3 p-4 ${ticketId === null && (cropping || image) ? 'max-w-[96vw] sm:max-w-[min(96vw,64rem)]' : 'max-w-lg sm:max-w-lg'}`}\n data-testid=\"report-problem-dialog\"\n >\n <div className=\"pr-8\">\n <DialogTitle>\n {ticketId === null ? 'Contact support' : 'Support request sent'}\n </DialogTitle>\n <DialogDescription>\n {ticketId !== null\n ? 'We have received your request. Track updates in My requests.'\n : cropping\n ? 'Crop the screenshot to the part that shows the problem. Only the cropped part is kept.'\n : image\n ? 'Black out anything confidential before sending. Blacked-out areas are removed from the image.'\n : 'Tell us what you need help with. You can choose to add a screenshot.'}\n </DialogDescription>\n </div>\n\n {ticketId !== null && (\n <div className=\"flex items-center justify-between gap-4 rounded-md border border-border bg-muted p-4\">\n <p className=\"font-medium\" data-testid=\"report-ticket-receipt\">\n Ticket number: {ticketReference ?? `#${ticketId}`}\n </p>\n <Button onClick={onClose} data-testid=\"report-receipt-close\">\n Close\n </Button>\n </div>\n )}\n\n {cropping ? (\n <div className=\"grid gap-2\" hidden={ticketId !== null}>\n <ImageCropper\n key={frameSize ? 'sized' : 'unsized'}\n image={frameUrl}\n aspect={\n frameSize\n ? frameSize.width / frameSize.height\n : window.innerWidth / window.innerHeight\n }\n maxZoom={6}\n cropAreaClassName=\"h-[50vh] min-h-64\"\n onCropComplete={(area) => setCropArea(area)}\n />\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button\n variant=\"ghost\"\n onClick={() => onImageChange(null)}\n data-testid=\"report-remove-screenshot\"\n >\n <Trash2 className=\"mr-1 size-4\" /> Remove screenshot\n </Button>\n <Button variant=\"ghost\" onClick={onRetake} data-testid=\"report-retake\">\n <Camera className=\"mr-1 size-4\" /> Retake\n </Button>\n <Button\n disabled={!cropArea || busy}\n onClick={() => void applyCrop()}\n data-testid=\"report-use-crop\"\n >\n <Crop className=\"mr-1 size-4\" /> Use this crop\n </Button>\n </div>\n </div>\n ) : imageUrl ? (\n <div className=\"grid gap-2\" hidden={ticketId !== null}>\n <div className=\"flex min-h-0 justify-center overflow-hidden rounded-md bg-muted p-2\">\n <div className=\"relative max-w-full\">\n <img\n ref={imgRef}\n src={imageUrl}\n alt=\"Screenshot of the problem\"\n className=\"block max-h-[45vh] max-w-full select-none object-contain\"\n draggable={false}\n onLoad={(event) =>\n setNatural({\n width: event.currentTarget.naturalWidth,\n height: event.currentTarget.naturalHeight,\n })\n }\n data-testid=\"report-screenshot\"\n />\n <div\n className=\"absolute inset-0 cursor-crosshair touch-none\"\n onPointerDown={(event) => {\n const point = pointFrom(event)\n if (!point) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n dragStart.current = point\n setDraft({ kind: 'redact', ...point, w: 0, h: 0 })\n }}\n onPointerMove={(event) => {\n const point = pointFrom(event)\n if (dragStart.current && point && natural)\n setDraft({\n kind: 'redact',\n ...rectFromDrag(dragStart.current, point, natural),\n })\n }}\n onPointerUp={() => {\n if (draft && isMeaningfulRect(draft)) setMarks((m) => [...m, draft])\n dragStart.current = null\n setDraft(null)\n }}\n data-testid=\"report-draw-surface\"\n >\n {shown.map((mark, i) => (\n <div\n key={i}\n className=\"pointer-events-none absolute bg-black\"\n style={percent(mark)}\n data-testid=\"report-mark-redact\"\n />\n ))}\n </div>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center justify-end gap-2 text-sm\">\n <span className=\"mr-auto flex items-center gap-1 text-muted-foreground\">\n <EyeOff className=\"size-4\" /> Drag over anything confidential to black it out.\n </span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={!marks.length}\n onClick={() => setMarks((m) => m.slice(0, -1))}\n data-testid=\"report-undo\"\n >\n <Undo2 className=\"mr-1 size-4\" /> Undo\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => onImageChange(null)}\n data-testid=\"report-remove-screenshot\"\n >\n <Trash2 className=\"mr-1 size-4\" /> Remove\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={onRetake} data-testid=\"report-retake\">\n <Camera className=\"mr-1 size-4\" /> Retake\n </Button>\n </div>\n </div>\n ) : (\n <div\n className=\"flex flex-wrap items-center gap-3 rounded-md border border-dashed border-border p-3 text-sm\"\n hidden={ticketId !== null}\n >\n <span className=\"text-muted-foreground\">No screenshot attached.</span>\n {canCaptureScreen() ? (\n <Button size=\"sm\" variant=\"outline\" onClick={onRetake} data-testid=\"report-retake\">\n <Camera className=\"mr-1 size-4\" /> Take a screenshot\n </Button>\n ) : (\n <label className=\"inline-flex cursor-pointer items-center gap-1 font-medium text-primary\">\n <ImageUp className=\"size-4\" /> Choose a screenshot\n <input\n type=\"file\"\n accept=\"image/*\"\n className=\"sr-only\"\n onChange={(event) => {\n const file = event.target.files?.[0]\n if (file) onImageChange(file)\n }}\n data-testid=\"report-choose-image\"\n />\n </label>\n )}\n </div>\n )}\n\n <label className=\"grid gap-1 text-sm\" hidden={ticketId !== null}>\n <span className=\"font-medium\">What went wrong?</span>\n <Textarea\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n rows={3}\n placeholder=\"What did you do, and what happened instead?\"\n data-testid=\"report-description\"\n />\n </label>\n\n {failure && (\n <p role=\"alert\" className=\"text-sm text-destructive\" hidden={ticketId !== null}>\n {failure}\n </p>\n )}\n <div className=\"flex flex-wrap items-center justify-end gap-2\" hidden={ticketId !== null}>\n <span className=\"mr-auto text-xs text-muted-foreground\">{footnote}</span>\n <Button variant=\"ghost\" onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button\n disabled={!onSubmit || !description.trim() || busy || cropping || (!!image && !natural)}\n onClick={() => void submit()}\n data-testid=\"report-send\"\n >\n {busy ? 'Sending\u2026' : 'Send to support'}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/** Support form with optional screenshot evidence and redaction. */\nimport React, { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport { Camera, Crop, EyeOff, ImageUp, Trash2, Undo2 } from 'lucide-react'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogTitle,\n Textarea,\n} from '@arbidocs/react/ui'\nimport { ImageCropper, cropImage, type CropArea } from '@arbidocs/react'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { canCaptureScreen } from './screen-capture'\nimport {\n isMeaningfulRect,\n rectFromDrag,\n renderAnnotatedScreenshot,\n toImagePoint,\n type Mark,\n} from './screenshot-annotations'\n\nexport interface ReportProblemOptions {\n /** @deprecated No image download is created. Retained for source compatibility. */\n fileNamePrefix?: string\n onSubmit?: (report: {\n description: string\n image?: Blob\n idempotencyKey: string\n }) => Promise<{ ticket_id: number; reference?: string }>\n /** A line beside the actions: where reports go (the site's own words). Default: none. */\n footnote?: ReactNode\n}\n\nexport interface ReportProblemDialogProps extends ReportProblemOptions {\n open: boolean\n /** The captured frame. Dropped once a crop is chosen. */\n frame: Blob | null\n /** The kept screenshot (the crop, or a picked file). */\n image: Blob | null\n onCropped: (crop: Blob) => void\n onImageChange: (image: Blob | null) => void\n onRetake: () => void\n onClose: () => void\n}\n\nexport function ReportProblemDialog({\n open,\n frame,\n image,\n onCropped,\n onImageChange,\n onRetake,\n onClose,\n onSubmit,\n footnote,\n}: ReportProblemDialogProps): React.ReactElement {\n const { notify } = useBlocksEnvironment()\n const [description, setDescription] = useState('')\n const [marks, setMarks] = useState<Mark[]>([])\n const [draft, setDraft] = useState<Mark | null>(null)\n const [natural, setNatural] = useState<{ width: number; height: number } | null>(null)\n const [frameSize, setFrameSize] = useState<{ width: number; height: number } | null>(null)\n const [cropArea, setCropArea] = useState<CropArea | null>(null)\n const [cropping, setCropping] = useState(false)\n const [busy, setBusy] = useState(false)\n const [failure, setFailure] = useState<string | null>(null)\n const [ticketReference, setTicketReference] = useState<string | null>(null)\n const [ticketId, setTicketId] = useState<number | null>(null)\n const [submissionId] = useState(() => crypto.randomUUID())\n const imgRef = useRef<HTMLImageElement>(null)\n const dragStart = useRef<{ x: number; y: number } | null>(null)\n\n const screenshot = image ?? frame\n const imageUrl = useMemo(\n () => (screenshot ? URL.createObjectURL(screenshot) : null),\n [screenshot]\n )\n useEffect(() => () => void (imageUrl && URL.revokeObjectURL(imageUrl)), [imageUrl])\n\n // The frame's size picks the \"Screen\" crop shape.\n useEffect(() => {\n setFrameSize(null)\n if (!imageUrl) return\n const img = new Image()\n img.onload = () => setFrameSize({ width: img.naturalWidth, height: img.naturalHeight })\n img.src = imageUrl\n }, [imageUrl])\n\n // A new screenshot starts new marks; closing starts a new report.\n useEffect(() => {\n setMarks([])\n setDraft(null)\n setNatural(null)\n setCropping(false)\n setCropArea(null)\n }, [screenshot])\n\n const applyCrop = async () => {\n if (!imageUrl || !cropArea) return\n setBusy(true)\n try {\n const { blob } = await cropImage(imageUrl, cropArea, { mimeType: 'image/png' })\n onCropped(blob)\n setCropping(false)\n } catch {\n notify(\"Couldn't crop the screenshot. Please try again.\", 'error')\n } finally {\n setBusy(false)\n }\n }\n\n const pointFrom = (event: React.PointerEvent) => {\n const img = imgRef.current\n if (!img || !natural) return null\n return toImagePoint(\n { x: event.clientX, y: event.clientY },\n img.getBoundingClientRect(),\n natural\n )\n }\n\n const renderImage = async (): Promise<Blob | null> => {\n if (!screenshot || !natural) return null\n const bitmap = await createImageBitmap(screenshot)\n try {\n return await renderAnnotatedScreenshot(bitmap, marks, { happened: description, expected: '' })\n } finally {\n bitmap.close()\n }\n }\n\n const submit = async () => {\n if (!onSubmit || !description.trim()) return\n setBusy(true)\n setFailure(null)\n try {\n const rendered = screenshot ? await renderImage() : null\n if (screenshot && !rendered) throw new Error('The screenshot is not ready. Please try again.')\n const receipt = await onSubmit({\n description: description.trim(),\n image: rendered ?? undefined,\n idempotencyKey: submissionId,\n })\n setTicketId(receipt.ticket_id)\n setTicketReference(\n receipt.reference &&\n /^[a-z0-9-]+-[0-9]+$/.test(receipt.reference) &&\n receipt.reference.endsWith(`-${receipt.ticket_id}`)\n ? receipt.reference\n : null\n )\n } catch (error) {\n setFailure(\n error instanceof Error ? error.message : 'Could not send your request. Please try again.'\n )\n } finally {\n setBusy(false)\n }\n }\n\n const shown = draft ? [...marks, draft] : marks\n const percent = (mark: Mark) =>\n natural\n ? {\n left: `${(mark.x / natural.width) * 100}%`,\n top: `${(mark.y / natural.height) * 100}%`,\n width: `${(mark.w / natural.width) * 100}%`,\n height: `${(mark.h / natural.height) * 100}%`,\n }\n : undefined\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && !busy && onClose()}>\n <DialogContent\n className={`flex max-h-[92vh] w-[96vw] flex-col gap-3 p-4 ${ticketId === null && screenshot ? 'max-w-[96vw] sm:max-w-[min(96vw,64rem)]' : 'max-w-lg sm:max-w-lg'}`}\n data-testid=\"report-problem-dialog\"\n >\n <div className=\"pr-8\">\n <DialogTitle>\n {ticketId === null ? 'Contact support' : 'Support request sent'}\n </DialogTitle>\n <DialogDescription>\n {ticketId !== null\n ? 'We have received your request. Track updates in My requests.'\n : cropping\n ? 'Crop the screenshot to the part that shows the problem. Only the cropped part is kept.'\n : screenshot\n ? 'Black out anything confidential before sending. Blacked-out areas are removed from the image.'\n : 'Tell us what you need help with. You can choose to add a screenshot.'}\n </DialogDescription>\n </div>\n\n {ticketId !== null && (\n <div className=\"flex items-center justify-between gap-4 rounded-md border border-border bg-muted p-4\">\n <p className=\"font-medium\" data-testid=\"report-ticket-receipt\">\n Ticket number: {ticketReference ?? `#${ticketId}`}\n </p>\n <Button onClick={onClose} data-testid=\"report-receipt-close\">\n Close\n </Button>\n </div>\n )}\n\n {cropping && imageUrl ? (\n <div className=\"grid gap-2\" hidden={ticketId !== null}>\n <ImageCropper\n key={frameSize ? 'sized' : 'unsized'}\n image={imageUrl}\n aspect={\n frameSize\n ? frameSize.width / frameSize.height\n : window.innerWidth / window.innerHeight\n }\n maxZoom={6}\n cropAreaClassName=\"h-[50vh] min-h-64\"\n onCropComplete={(area) => setCropArea(area)}\n />\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button\n variant=\"ghost\"\n onClick={() => setCropping(false)}\n data-testid=\"report-cancel-crop\"\n >\n Cancel crop\n </Button>\n <Button\n variant=\"ghost\"\n onClick={() => onImageChange(null)}\n data-testid=\"report-remove-screenshot\"\n >\n <Trash2 className=\"mr-1 size-4\" /> Remove screenshot\n </Button>\n <Button variant=\"ghost\" onClick={onRetake} data-testid=\"report-retake\">\n <Camera className=\"mr-1 size-4\" /> Retake\n </Button>\n <Button\n disabled={!cropArea || busy}\n onClick={() => void applyCrop()}\n data-testid=\"report-use-crop\"\n >\n <Crop className=\"mr-1 size-4\" /> Use this crop\n </Button>\n </div>\n </div>\n ) : imageUrl ? (\n <div className=\"grid gap-2\" hidden={ticketId !== null}>\n <div className=\"flex min-h-0 justify-center overflow-hidden rounded-md bg-muted p-2\">\n <div className=\"relative max-w-full\">\n <img\n ref={imgRef}\n src={imageUrl}\n alt=\"Screenshot of the problem\"\n className=\"block max-h-[45vh] max-w-full select-none object-contain\"\n draggable={false}\n onLoad={(event) =>\n setNatural({\n width: event.currentTarget.naturalWidth,\n height: event.currentTarget.naturalHeight,\n })\n }\n data-testid=\"report-screenshot\"\n />\n <div\n className=\"absolute inset-0 cursor-crosshair touch-none\"\n onPointerDown={(event) => {\n const point = pointFrom(event)\n if (!point) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n dragStart.current = point\n setDraft({ kind: 'redact', ...point, w: 0, h: 0 })\n }}\n onPointerMove={(event) => {\n const point = pointFrom(event)\n if (dragStart.current && point && natural)\n setDraft({\n kind: 'redact',\n ...rectFromDrag(dragStart.current, point, natural),\n })\n }}\n onPointerUp={() => {\n if (draft && isMeaningfulRect(draft)) setMarks((m) => [...m, draft])\n dragStart.current = null\n setDraft(null)\n }}\n data-testid=\"report-draw-surface\"\n >\n {shown.map((mark, i) => (\n <div\n key={i}\n className=\"pointer-events-none absolute bg-black\"\n style={percent(mark)}\n data-testid=\"report-mark-redact\"\n />\n ))}\n </div>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center justify-end gap-2 text-sm\">\n <span className=\"mr-auto flex items-center gap-1 text-muted-foreground\">\n <EyeOff className=\"size-4\" /> Drag over anything confidential to black it out.\n </span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={!!marks.length}\n title={marks.length ? 'Undo blacked-out areas before cropping' : undefined}\n onClick={() => {\n setCropArea(null)\n setCropping(true)\n }}\n data-testid=\"report-crop\"\n >\n <Crop className=\"mr-1 size-4\" /> Crop\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={!marks.length}\n onClick={() => setMarks((m) => m.slice(0, -1))}\n data-testid=\"report-undo\"\n >\n <Undo2 className=\"mr-1 size-4\" /> Undo\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => onImageChange(null)}\n data-testid=\"report-remove-screenshot\"\n >\n <Trash2 className=\"mr-1 size-4\" /> Remove\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={onRetake} data-testid=\"report-retake\">\n <Camera className=\"mr-1 size-4\" /> Retake\n </Button>\n </div>\n </div>\n ) : (\n <div\n className=\"flex flex-wrap items-center gap-3 rounded-md border border-dashed border-border p-3 text-sm\"\n hidden={ticketId !== null}\n >\n <span className=\"text-muted-foreground\">No screenshot attached.</span>\n {canCaptureScreen() ? (\n <Button size=\"sm\" variant=\"outline\" onClick={onRetake} data-testid=\"report-retake\">\n <Camera className=\"mr-1 size-4\" /> Take a screenshot\n </Button>\n ) : (\n <label className=\"inline-flex cursor-pointer items-center gap-1 font-medium text-primary\">\n <ImageUp className=\"size-4\" /> Choose a screenshot\n <input\n type=\"file\"\n accept=\"image/*\"\n className=\"sr-only\"\n onChange={(event) => {\n const file = event.target.files?.[0]\n if (file) onImageChange(file)\n }}\n data-testid=\"report-choose-image\"\n />\n </label>\n )}\n </div>\n )}\n\n <label className=\"grid gap-1 text-sm\" hidden={ticketId !== null}>\n <span className=\"font-medium\">What went wrong?</span>\n <Textarea\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n rows={3}\n placeholder=\"What did you do, and what happened instead?\"\n data-testid=\"report-description\"\n />\n </label>\n\n {failure && (\n <p role=\"alert\" className=\"text-sm text-destructive\" hidden={ticketId !== null}>\n {failure}\n </p>\n )}\n <div className=\"flex flex-wrap items-center justify-end gap-2\" hidden={ticketId !== null}>\n <span className=\"mr-auto text-xs text-muted-foreground\">{footnote}</span>\n <Button variant=\"ghost\" onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button\n disabled={\n !onSubmit || !description.trim() || busy || cropping || (!!screenshot && !natural)\n }\n onClick={() => void submit()}\n data-testid=\"report-send\"\n >\n {busy ? 'Sending\u2026' : 'Send to support'}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -18,7 +18,8 @@ import { useOpenWorkflow } from "./useOpenWorkflow.js";
|
|
|
18
18
|
import { useHost } from "../../provider/host.js";
|
|
19
19
|
import { useIntentRequest, workflowOpenRequest } from "../../provider/requests.js";
|
|
20
20
|
import {
|
|
21
|
-
ConnectedWorkflowBuilder
|
|
21
|
+
ConnectedWorkflowBuilder,
|
|
22
|
+
scheduledTaskBuilderValue
|
|
22
23
|
} from "../ConnectedWorkflowBuilder.js";
|
|
23
24
|
import { ConnectedWorkflowHistory } from "../ConnectedWorkflowHistory.js";
|
|
24
25
|
import { ConnectedWorkflowsTable } from "../ConnectedWorkflowsTable.js";
|
|
@@ -95,7 +96,13 @@ function ConnectedWorkflowsPage({
|
|
|
95
96
|
setHostTabId(null);
|
|
96
97
|
openHistory(workflow);
|
|
97
98
|
};
|
|
98
|
-
useIntentRequest(workflowOpenRequest, ({ workflowId, view }) => {
|
|
99
|
+
useIntentRequest(workflowOpenRequest, ({ workflowId, view, preset }) => {
|
|
100
|
+
if (!workflowId && preset) {
|
|
101
|
+
setRunningId(null);
|
|
102
|
+
setHostTabId(null);
|
|
103
|
+
openBuilder({ kind: "new", preset });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
99
106
|
if (view === "run") {
|
|
100
107
|
if (!workflowId) return;
|
|
101
108
|
closeBuilder();
|
|
@@ -216,7 +223,7 @@ function ConnectedWorkflowsPage({
|
|
|
216
223
|
ConnectedWorkflowBuilder,
|
|
217
224
|
{
|
|
218
225
|
workflowId: builder.kind === "saved" ? builder.id : void 0,
|
|
219
|
-
initial: template ? { workflow: template.workflow, trigger: DEFAULT_WORKFLOW_TRIGGER } : void 0,
|
|
226
|
+
initial: template ? { workflow: template.workflow, trigger: DEFAULT_WORKFLOW_TRIGGER } : builder.kind === "new" && builder.preset === "schedule" ? scheduledTaskBuilderValue() : void 0,
|
|
220
227
|
workflows: config?.stepWorkflows ?? webWorkflows,
|
|
221
228
|
renderIcon: config?.renderIcon,
|
|
222
229
|
onSaved: (id) => builderSaved(id, builderKey),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/workflows/ConnectedWorkflowsPage.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * The Workflows page, connected: one composition of the saved workflows' table\n * (`ConnectedWorkflowsTable`), the visual builder (`ConnectedWorkflowBuilder`) and a workflow's\n * history (`ConnectedWorkflowHistory`), on the shared selection lifecycle\n * (`useWorkflowsPageState`).\n *\n * - The table lists the saved workflows, with the host's catalogue (`workflows.catalogue` in the\n * host configuration), when it has one, above them in the host's categories.\n * - A workflow opens in the builder, which takes the page (Back returns to the list). A document\n * action (annotate or email documents), which runs without a thread, keeps its own editor.\n * - A workflow's history opens as a tab of the table, beside the host's own `tabs`.\n * - `openWorkflow(id, { view })` from anywhere opens the workflow's builder or history here; the\n * host only shows the page. `view: 'run'` on a catalogue entry opens its run page here\n * (`ConnectedWorkflowRunPage`, with the entry's project options).\n *\n * It navigates only through the environment: threads open with `openConversation`, an AI review's\n * result with `openFiles` (offered when the host has it), a catalogue entry runs with\n * `openWorkflow(id, { view: 'run' })`.\n */\n\nimport { useCallback, useMemo, useState, type ReactNode } from 'react'\nimport { ArrowLeft, Pencil, Play, Plus, Workflow as WorkflowIcon } from 'lucide-react'\nimport {\n useInvalidateTagData,\n useRefreshDocuments,\n useTags,\n useWorkflowRuns,\n type WorkflowResponse,\n} from '@arbidocs/react'\nimport { Button, formatFriendlyDate } from '@arbidocs/react/ui'\nimport { PageHeader, type PageHeaderProps } from '../../components/PageHeader'\nimport { describeTrigger } from '../../agents/tasks/taskHelpers'\nimport type { TaskRunsDialogLabels } from '../../agents/tasks/TaskRunsDialog'\nimport type { WorkflowListLabels } from '../../agents/tasks/workflowTableLabels'\nimport { WEB_WORKFLOWS } from '../../agents/workflows/catalogue/registry'\nimport { useNamedWorkflows } from './useNamedWorkflows'\nimport { DEFAULT_WORKFLOW_TRIGGER } from '../../workflow-canvas/trigger'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useOpenWorkflow } from './useOpenWorkflow'\nimport { useHost } from '../../provider/host'\nimport { useIntentRequest, workflowOpenRequest } from '../../provider/requests'\nimport {\n ConnectedWorkflowBuilder,\n type ConnectedWorkflowBuilderProps,\n} from '../ConnectedWorkflowBuilder'\nimport { ConnectedWorkflowHistory } from '../ConnectedWorkflowHistory'\nimport { ConnectedWorkflowsTable } from '../ConnectedWorkflowsTable'\nimport { ConnectedWorkflowRunPage } from './ConnectedWorkflowRunPage'\nimport { ConnectedTaskComposer } from '../tasks/ConnectedTaskComposer'\nimport { useRenderTaskPerson } from '../tasks/useTaskPeople'\nimport { useAiReviewStore } from '../files/store'\nimport { useWorkflowsPageState } from './useWorkflowsPageState'\nimport {\n DEFAULT_CONNECTED_WORKFLOWS_PAGE_LABELS,\n DEFAULT_WORKFLOWS_PAGE_TABLE_LABELS,\n WORKFLOWS_PAGE_TRIGGER_CHIP_LABELS,\n type ConnectedWorkflowsPageLabels,\n} from './workflowsPageLabels'\n\ntype WorkflowRun = NonNullable<ReturnType<typeof useWorkflowRuns>['data']>[number]\n\n/** A tab of the host's beside the saved workflows (e.g. the agents that run them). */\nexport interface WorkflowsPageTab {\n id: string\n label: string\n icon?: ReactNode\n content: ReactNode\n}\n\n/** The table's own tab (the saved workflows). */\nconst LIST_TAB = 'automations'\n\nexport interface ConnectedWorkflowsPageProps {\n /** A host deep link to a workflow's history (e.g. a `?workflow=` URL), taken once it loads. */\n historyWorkflowId?: string | null\n onHistoryWorkflowConsumed?: () => void\n /** A title block over the table (eyebrow, title, description). Absent: none. */\n pageHeader?: Omit<PageHeaderProps, 'actions' | 'tabs'>\n /** The host's tabs beside the saved workflows, before any open history. */\n tabs?: WorkflowsPageTab[]\n /**\n * One of the host's `tabs` to show instead of the saved workflows (a host deep link such as\n * `?tab=agents`): on mount, and again whenever it changes to another tab. Unknown ids show the\n * saved workflows.\n */\n initialTabId?: string | null\n /** The builder's words and options. */\n builderProps?: Pick<\n ConnectedWorkflowBuilderProps,\n | 'labels'\n | 'canvasLabels'\n | 'inspectorLabels'\n | 'triggerLabels'\n | 'connectedLabels'\n | 'eventKinds'\n | 'compose'\n >\n labels?: Partial<ConnectedWorkflowsPageLabels>\n tableLabels?: Partial<WorkflowListLabels>\n historyLabels?: Partial<TaskRunsDialogLabels>\n testId?: string\n}\n\nexport function ConnectedWorkflowsPage({\n historyWorkflowId,\n onHistoryWorkflowConsumed,\n pageHeader,\n tabs,\n initialTabId,\n builderProps,\n labels,\n tableLabels,\n historyLabels,\n testId = 'workflows-page',\n}: ConnectedWorkflowsPageProps) {\n // The default step catalogue in the host's assistant name.\n const webWorkflows = useNamedWorkflows(WEB_WORKFLOWS)\n const l = { ...DEFAULT_CONNECTED_WORKFLOWS_PAGE_LABELS, ...labels }\n const host = useHost()\n const config = host.workflows\n const { openConversation, openFiles } = useBlocksEnvironment()\n const openWorkflow = useOpenWorkflow()\n const renderPerson = useRenderTaskPerson()\n const refreshDocuments = useRefreshDocuments()\n const invalidateTags = useInvalidateTagData()\n const { data: tags = [] } = useTags()\n const [editing, setEditing] = useState<WorkflowResponse | null>(null)\n const [editorOpen, setEditorOpen] = useState(false)\n const [hostTabId, setHostTabId] = useState<string | null>(initialTabId ?? null)\n // The host's link is applied again when it names another tab (the page stays mounted when the\n // host's address changes), never over the person's own choice of tab otherwise.\n const [appliedTabId, setAppliedTabId] = useState(initialTabId)\n if (initialTabId !== appliedTabId) {\n setAppliedTabId(initialTabId)\n if (initialTabId) setHostTabId(initialTabId)\n }\n // The catalogue entry being run (`openWorkflow(id, { view: 'run' })`).\n const [runningId, setRunningId] = useState<string | null>(null)\n // A workflow `openWorkflow(id, { view: 'runs' })` asked for, until it has loaded.\n const [requestedHistoryId, setRequestedHistoryId] = useState<string | null>(null)\n const consumeHistory = useCallback(() => {\n if (requestedHistoryId) setRequestedHistoryId(null)\n else onHistoryWorkflowConsumed?.()\n }, [requestedHistoryId, onHistoryWorkflowConsumed])\n const {\n workflows,\n builder,\n builderKey,\n openBuilder,\n closeBuilder,\n builderSaved,\n historyFor: runsFor,\n historyTabs,\n activeHistoryId,\n openHistory,\n closeHistoryTab,\n selectHistoryTab,\n } = useWorkflowsPageState({\n historyWorkflowId: requestedHistoryId ?? historyWorkflowId,\n onHistoryWorkflowConsumed: consumeHistory,\n })\n const listLabels = useMemo(\n () => ({ ...DEFAULT_WORKFLOWS_PAGE_TABLE_LABELS, ...tableLabels }),\n [tableLabels]\n )\n const hostTab = tabs?.find((tab) => tab.id === hostTabId)\n const showHistory = (workflow: WorkflowResponse | null) => {\n setHostTabId(null)\n openHistory(workflow)\n }\n\n useIntentRequest(workflowOpenRequest, ({ workflowId, view }) => {\n if (view === 'run') {\n if (!workflowId) return\n closeBuilder()\n setRunningId(workflowId)\n return\n }\n setRunningId(null)\n if (workflowId && view !== 'runs') {\n openBuilder({ kind: 'saved', id: workflowId })\n return\n }\n closeBuilder()\n setHostTabId(null)\n if (workflowId) setRequestedHistoryId(workflowId)\n else selectHistoryTab(null)\n })\n\n const edit = (workflow: WorkflowResponse) => {\n showHistory(workflow)\n // Every workflow opens in the visual builder: stepped ones as their steps, a classic one (a\n // single prompt or skill) as one agent step, a reminder with no steps yet. Only document actions\n // (annotate / email documents), which run without a thread, keep their own editor.\n if (!workflow.action) {\n openBuilder({ kind: 'saved', id: workflow.external_id })\n return\n }\n setEditing(workflow)\n setEditorOpen(true)\n }\n\n // An AI review's result: its documents re-read, Files narrowed to its label, its fields shown.\n const openResult = async (execution: WorkflowRun) => {\n await refreshDocuments(execution.document_ext_ids)\n await invalidateTags()\n const fieldIds =\n (execution.result?.tag_ext_ids as string[] | undefined) ?? runsFor?.action?.tag_ext_ids ?? []\n useAiReviewStore.getState().setRun({\n docIds: execution.document_ext_ids,\n tags: fieldIds.map((fieldId) => ({\n id: fieldId,\n name: tags.find((tag) => tag.external_id === fieldId)?.name ?? 'Review',\n })),\n })\n useAiReviewStore.getState().setOpen(true)\n openFiles(\n execution.label_ext_id ? { filters: { [execution.label_ext_id]: 'include' } } : undefined\n )\n }\n const renderErrorReport = config?.renderErrorReport\n\n const running = runningId\n ? config?.catalogue?.find((entry) => entry.workflow.id === runningId)\n : undefined\n if (running && running.runnable !== false) {\n const stepWorkflows = config?.stepWorkflows ?? webWorkflows\n return (\n // The page in the Workflows page's frame: padded, scrolling.\n <div className=\"h-full min-h-0 w-full overflow-auto p-4 sm:p-6\">\n <ConnectedWorkflowRunPage\n key={running.workflow.id}\n workflow={running.workflow}\n projectFrom={running.projectFrom}\n inputsFromProject={running.inputsFromProject}\n onProjectCreated={running.onProjectCreated}\n options={{ resolveWorkflow: (id) => stepWorkflows.find((w) => w.id === id) }}\n renderIcon={config?.renderIcon}\n onOpenConversation={(id) => openConversation(id)}\n pageHeader={{\n eyebrow: running.category,\n leading: (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setRunningId(null)}\n aria-label={l.back}\n title={l.back}\n data-testid=\"workflow-run-back\"\n >\n <ArrowLeft className=\"size-4\" />\n </Button>\n ),\n }}\n testIdPrefix=\"workflow-run\"\n />\n </div>\n )\n }\n\n if (builder) {\n const template =\n builder.kind === 'template'\n ? config?.catalogue?.find((entry) => entry.workflow.id === builder.id)\n : undefined\n return (\n <div\n className=\"flex h-full min-h-0 flex-col gap-3 overflow-auto p-4\"\n data-testid=\"workflow-builder\"\n >\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={closeBuilder}\n data-testid=\"workflow-builder-back\"\n >\n <ArrowLeft className=\"size-4\" /> {l.back}\n </Button>\n {template && template.runnable !== false && openWorkflow && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => openWorkflow(template.workflow.id, { view: 'run' })}\n data-testid=\"workflow-builder-run\"\n >\n <Play className=\"size-4\" /> {l.runTemplate}\n </Button>\n )}\n </div>\n <ConnectedWorkflowBuilder\n key={builderKey}\n workflowId={builder.kind === 'saved' ? builder.id : undefined}\n initial={\n template\n ? { workflow: template.workflow, trigger: DEFAULT_WORKFLOW_TRIGGER }\n : undefined\n }\n workflows={config?.stepWorkflows ?? webWorkflows}\n renderIcon={config?.renderIcon}\n onSaved={(id) => builderSaved(id, builderKey)}\n testId=\"workflow-builder-canvas\"\n {...builderProps}\n />\n </div>\n )\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\" data-testid={testId}>\n {pageHeader && <PageHeader {...pageHeader} />}\n <ConnectedWorkflowsTable\n onOpenTemplate={(id) => openBuilder({ kind: 'template', id })}\n tabs={[\n ...(tabs ?? []).map(({ id, label, icon }) => ({ id, label, icon })),\n ...historyTabs.map((workflow) => ({\n id: workflow.external_id,\n label: workflow.name,\n icon: <WorkflowIcon className=\"size-3.5\" />,\n onClose: () => {\n closeHistoryTab(workflow.external_id)\n if (activeHistoryId === workflow.external_id) setEditorOpen(false)\n },\n })),\n ]}\n activeTabId={hostTab?.id ?? activeHistoryId ?? LIST_TAB}\n onTabChange={(id) => {\n if (tabs?.some((tab) => tab.id === id)) {\n setHostTabId(id)\n return\n }\n setHostTabId(null)\n selectHistoryTab(id === LIST_TAB ? null : id)\n if (editorOpen) {\n const workflow = workflows.find((item) => item.external_id === id)\n if (workflow) setEditing(workflow)\n else setEditorOpen(false)\n }\n }}\n content={\n hostTab ? (\n hostTab.content\n ) : runsFor ? (\n <ConnectedWorkflowHistory\n inline\n hideHeader\n workflow={runsFor}\n onOpenChange={(open) => !open && openHistory(null)}\n formatDate={formatFriendlyDate}\n labels={historyLabels}\n renderErrorReport={\n renderErrorReport &&\n ((run) =>\n renderErrorReport(\n JSON.stringify(\n {\n workflow_ext_id: runsFor.external_id,\n run_id: run.id,\n status: run.status,\n error: run.error,\n },\n null,\n 2\n )\n ))\n }\n // Offered where the host shows files (its `openFiles`).\n onOpenResult={host.environment?.openFiles ? openResult : undefined}\n />\n ) : undefined\n }\n automationsOnly\n gridId=\"automations-grid-v1\"\n describeTrigger={(workflow) =>\n workflow.on_demand ? l.manualTrigger : describeTrigger(workflow)\n }\n formatDate={formatFriendlyDate}\n renderPerson={renderPerson}\n onViewRuns={showHistory}\n onOpenRow={edit}\n onEdit={edit}\n triggerChipLabels={WORKFLOWS_PAGE_TRIGGER_CHIP_LABELS}\n labels={listLabels}\n // A narrow row (a phone) keeps the actions' icons, so the host's tabs keep their room.\n header={(isCompact) => (\n <>\n {runsFor && !hostTab && (\n <Button\n variant=\"ghost\"\n onClick={() => edit(runsFor)}\n aria-label={l.editAria}\n title={isCompact ? l.edit : undefined}\n >\n <Pencil className={isCompact ? 'size-4' : 'mr-1.5 size-4'} />\n {!isCompact && l.edit}\n </Button>\n )}\n <Button\n onClick={() => openBuilder({ kind: 'new' })}\n aria-label={isCompact ? l.newWorkflow : undefined}\n title={isCompact ? l.newWorkflow : undefined}\n data-testid=\"new-workflow-button\"\n >\n <Plus className={isCompact ? 'size-4' : 'mr-1.5 size-4'} />\n {!isCompact && l.newWorkflow}\n </Button>\n </>\n )}\n />\n <ConnectedTaskComposer\n kind=\"workflow\"\n saveOnly\n startLater\n open={editorOpen}\n onOpenChange={setEditorOpen}\n workflow={editing}\n />\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\n * The Workflows page, connected: one composition of the saved workflows' table\n * (`ConnectedWorkflowsTable`), the visual builder (`ConnectedWorkflowBuilder`) and a workflow's\n * history (`ConnectedWorkflowHistory`), on the shared selection lifecycle\n * (`useWorkflowsPageState`).\n *\n * - The table lists the saved workflows, with the host's catalogue (`workflows.catalogue` in the\n * host configuration), when it has one, above them in the host's categories.\n * - A workflow opens in the builder, which takes the page (Back returns to the list). A document\n * action (annotate or email documents), which runs without a thread, keeps its own editor.\n * - A workflow's history opens as a tab of the table, beside the host's own `tabs`.\n * - `openWorkflow(id, { view })` from anywhere opens the workflow's builder or history here; the\n * host only shows the page. `view: 'run'` on a catalogue entry opens its run page here\n * (`ConnectedWorkflowRunPage`, with the entry's project options).\n *\n * It navigates only through the environment: threads open with `openConversation`, an AI review's\n * result with `openFiles` (offered when the host has it), a catalogue entry runs with\n * `openWorkflow(id, { view: 'run' })`.\n */\n\nimport { useCallback, useMemo, useState, type ReactNode } from 'react'\nimport { ArrowLeft, Pencil, Play, Plus, Workflow as WorkflowIcon } from 'lucide-react'\nimport {\n useInvalidateTagData,\n useRefreshDocuments,\n useTags,\n useWorkflowRuns,\n type WorkflowResponse,\n} from '@arbidocs/react'\nimport { Button, formatFriendlyDate } from '@arbidocs/react/ui'\nimport { PageHeader, type PageHeaderProps } from '../../components/PageHeader'\nimport { describeTrigger } from '../../agents/tasks/taskHelpers'\nimport type { TaskRunsDialogLabels } from '../../agents/tasks/TaskRunsDialog'\nimport type { WorkflowListLabels } from '../../agents/tasks/workflowTableLabels'\nimport { WEB_WORKFLOWS } from '../../agents/workflows/catalogue/registry'\nimport { useNamedWorkflows } from './useNamedWorkflows'\nimport { DEFAULT_WORKFLOW_TRIGGER } from '../../workflow-canvas/trigger'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useOpenWorkflow } from './useOpenWorkflow'\nimport { useHost } from '../../provider/host'\nimport { useIntentRequest, workflowOpenRequest } from '../../provider/requests'\nimport {\n ConnectedWorkflowBuilder,\n scheduledTaskBuilderValue,\n type ConnectedWorkflowBuilderProps,\n} from '../ConnectedWorkflowBuilder'\nimport { ConnectedWorkflowHistory } from '../ConnectedWorkflowHistory'\nimport { ConnectedWorkflowsTable } from '../ConnectedWorkflowsTable'\nimport { ConnectedWorkflowRunPage } from './ConnectedWorkflowRunPage'\nimport { ConnectedTaskComposer } from '../tasks/ConnectedTaskComposer'\nimport { useRenderTaskPerson } from '../tasks/useTaskPeople'\nimport { useAiReviewStore } from '../files/store'\nimport { useWorkflowsPageState } from './useWorkflowsPageState'\nimport {\n DEFAULT_CONNECTED_WORKFLOWS_PAGE_LABELS,\n DEFAULT_WORKFLOWS_PAGE_TABLE_LABELS,\n WORKFLOWS_PAGE_TRIGGER_CHIP_LABELS,\n type ConnectedWorkflowsPageLabels,\n} from './workflowsPageLabels'\n\ntype WorkflowRun = NonNullable<ReturnType<typeof useWorkflowRuns>['data']>[number]\n\n/** A tab of the host's beside the saved workflows (e.g. the agents that run them). */\nexport interface WorkflowsPageTab {\n id: string\n label: string\n icon?: ReactNode\n content: ReactNode\n}\n\n/** The table's own tab (the saved workflows). */\nconst LIST_TAB = 'automations'\n\nexport interface ConnectedWorkflowsPageProps {\n /** A host deep link to a workflow's history (e.g. a `?workflow=` URL), taken once it loads. */\n historyWorkflowId?: string | null\n onHistoryWorkflowConsumed?: () => void\n /** A title block over the table (eyebrow, title, description). Absent: none. */\n pageHeader?: Omit<PageHeaderProps, 'actions' | 'tabs'>\n /** The host's tabs beside the saved workflows, before any open history. */\n tabs?: WorkflowsPageTab[]\n /**\n * One of the host's `tabs` to show instead of the saved workflows (a host deep link such as\n * `?tab=agents`): on mount, and again whenever it changes to another tab. Unknown ids show the\n * saved workflows.\n */\n initialTabId?: string | null\n /** The builder's words and options. */\n builderProps?: Pick<\n ConnectedWorkflowBuilderProps,\n | 'labels'\n | 'canvasLabels'\n | 'inspectorLabels'\n | 'triggerLabels'\n | 'connectedLabels'\n | 'eventKinds'\n | 'compose'\n >\n labels?: Partial<ConnectedWorkflowsPageLabels>\n tableLabels?: Partial<WorkflowListLabels>\n historyLabels?: Partial<TaskRunsDialogLabels>\n testId?: string\n}\n\nexport function ConnectedWorkflowsPage({\n historyWorkflowId,\n onHistoryWorkflowConsumed,\n pageHeader,\n tabs,\n initialTabId,\n builderProps,\n labels,\n tableLabels,\n historyLabels,\n testId = 'workflows-page',\n}: ConnectedWorkflowsPageProps) {\n // The default step catalogue in the host's assistant name.\n const webWorkflows = useNamedWorkflows(WEB_WORKFLOWS)\n const l = { ...DEFAULT_CONNECTED_WORKFLOWS_PAGE_LABELS, ...labels }\n const host = useHost()\n const config = host.workflows\n const { openConversation, openFiles } = useBlocksEnvironment()\n const openWorkflow = useOpenWorkflow()\n const renderPerson = useRenderTaskPerson()\n const refreshDocuments = useRefreshDocuments()\n const invalidateTags = useInvalidateTagData()\n const { data: tags = [] } = useTags()\n const [editing, setEditing] = useState<WorkflowResponse | null>(null)\n const [editorOpen, setEditorOpen] = useState(false)\n const [hostTabId, setHostTabId] = useState<string | null>(initialTabId ?? null)\n // The host's link is applied again when it names another tab (the page stays mounted when the\n // host's address changes), never over the person's own choice of tab otherwise.\n const [appliedTabId, setAppliedTabId] = useState(initialTabId)\n if (initialTabId !== appliedTabId) {\n setAppliedTabId(initialTabId)\n if (initialTabId) setHostTabId(initialTabId)\n }\n // The catalogue entry being run (`openWorkflow(id, { view: 'run' })`).\n const [runningId, setRunningId] = useState<string | null>(null)\n // A workflow `openWorkflow(id, { view: 'runs' })` asked for, until it has loaded.\n const [requestedHistoryId, setRequestedHistoryId] = useState<string | null>(null)\n const consumeHistory = useCallback(() => {\n if (requestedHistoryId) setRequestedHistoryId(null)\n else onHistoryWorkflowConsumed?.()\n }, [requestedHistoryId, onHistoryWorkflowConsumed])\n const {\n workflows,\n builder,\n builderKey,\n openBuilder,\n closeBuilder,\n builderSaved,\n historyFor: runsFor,\n historyTabs,\n activeHistoryId,\n openHistory,\n closeHistoryTab,\n selectHistoryTab,\n } = useWorkflowsPageState({\n historyWorkflowId: requestedHistoryId ?? historyWorkflowId,\n onHistoryWorkflowConsumed: consumeHistory,\n })\n const listLabels = useMemo(\n () => ({ ...DEFAULT_WORKFLOWS_PAGE_TABLE_LABELS, ...tableLabels }),\n [tableLabels]\n )\n const hostTab = tabs?.find((tab) => tab.id === hostTabId)\n const showHistory = (workflow: WorkflowResponse | null) => {\n setHostTabId(null)\n openHistory(workflow)\n }\n\n useIntentRequest(workflowOpenRequest, ({ workflowId, view, preset }) => {\n if (!workflowId && preset) {\n setRunningId(null)\n setHostTabId(null)\n openBuilder({ kind: 'new', preset })\n return\n }\n if (view === 'run') {\n if (!workflowId) return\n closeBuilder()\n setRunningId(workflowId)\n return\n }\n setRunningId(null)\n if (workflowId && view !== 'runs') {\n openBuilder({ kind: 'saved', id: workflowId })\n return\n }\n closeBuilder()\n setHostTabId(null)\n if (workflowId) setRequestedHistoryId(workflowId)\n else selectHistoryTab(null)\n })\n\n const edit = (workflow: WorkflowResponse) => {\n showHistory(workflow)\n // Every workflow opens in the visual builder: stepped ones as their steps, a classic one (a\n // single prompt or skill) as one agent step, a reminder with no steps yet. Only document actions\n // (annotate / email documents), which run without a thread, keep their own editor.\n if (!workflow.action) {\n openBuilder({ kind: 'saved', id: workflow.external_id })\n return\n }\n setEditing(workflow)\n setEditorOpen(true)\n }\n\n // An AI review's result: its documents re-read, Files narrowed to its label, its fields shown.\n const openResult = async (execution: WorkflowRun) => {\n await refreshDocuments(execution.document_ext_ids)\n await invalidateTags()\n const fieldIds =\n (execution.result?.tag_ext_ids as string[] | undefined) ?? runsFor?.action?.tag_ext_ids ?? []\n useAiReviewStore.getState().setRun({\n docIds: execution.document_ext_ids,\n tags: fieldIds.map((fieldId) => ({\n id: fieldId,\n name: tags.find((tag) => tag.external_id === fieldId)?.name ?? 'Review',\n })),\n })\n useAiReviewStore.getState().setOpen(true)\n openFiles(\n execution.label_ext_id ? { filters: { [execution.label_ext_id]: 'include' } } : undefined\n )\n }\n const renderErrorReport = config?.renderErrorReport\n\n const running = runningId\n ? config?.catalogue?.find((entry) => entry.workflow.id === runningId)\n : undefined\n if (running && running.runnable !== false) {\n const stepWorkflows = config?.stepWorkflows ?? webWorkflows\n return (\n // The page in the Workflows page's frame: padded, scrolling.\n <div className=\"h-full min-h-0 w-full overflow-auto p-4 sm:p-6\">\n <ConnectedWorkflowRunPage\n key={running.workflow.id}\n workflow={running.workflow}\n projectFrom={running.projectFrom}\n inputsFromProject={running.inputsFromProject}\n onProjectCreated={running.onProjectCreated}\n options={{ resolveWorkflow: (id) => stepWorkflows.find((w) => w.id === id) }}\n renderIcon={config?.renderIcon}\n onOpenConversation={(id) => openConversation(id)}\n pageHeader={{\n eyebrow: running.category,\n leading: (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setRunningId(null)}\n aria-label={l.back}\n title={l.back}\n data-testid=\"workflow-run-back\"\n >\n <ArrowLeft className=\"size-4\" />\n </Button>\n ),\n }}\n testIdPrefix=\"workflow-run\"\n />\n </div>\n )\n }\n\n if (builder) {\n const template =\n builder.kind === 'template'\n ? config?.catalogue?.find((entry) => entry.workflow.id === builder.id)\n : undefined\n return (\n <div\n className=\"flex h-full min-h-0 flex-col gap-3 overflow-auto p-4\"\n data-testid=\"workflow-builder\"\n >\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={closeBuilder}\n data-testid=\"workflow-builder-back\"\n >\n <ArrowLeft className=\"size-4\" /> {l.back}\n </Button>\n {template && template.runnable !== false && openWorkflow && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => openWorkflow(template.workflow.id, { view: 'run' })}\n data-testid=\"workflow-builder-run\"\n >\n <Play className=\"size-4\" /> {l.runTemplate}\n </Button>\n )}\n </div>\n <ConnectedWorkflowBuilder\n key={builderKey}\n workflowId={builder.kind === 'saved' ? builder.id : undefined}\n initial={\n template\n ? { workflow: template.workflow, trigger: DEFAULT_WORKFLOW_TRIGGER }\n : builder.kind === 'new' && builder.preset === 'schedule'\n ? scheduledTaskBuilderValue()\n : undefined\n }\n workflows={config?.stepWorkflows ?? webWorkflows}\n renderIcon={config?.renderIcon}\n onSaved={(id) => builderSaved(id, builderKey)}\n testId=\"workflow-builder-canvas\"\n {...builderProps}\n />\n </div>\n )\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\" data-testid={testId}>\n {pageHeader && <PageHeader {...pageHeader} />}\n <ConnectedWorkflowsTable\n onOpenTemplate={(id) => openBuilder({ kind: 'template', id })}\n tabs={[\n ...(tabs ?? []).map(({ id, label, icon }) => ({ id, label, icon })),\n ...historyTabs.map((workflow) => ({\n id: workflow.external_id,\n label: workflow.name,\n icon: <WorkflowIcon className=\"size-3.5\" />,\n onClose: () => {\n closeHistoryTab(workflow.external_id)\n if (activeHistoryId === workflow.external_id) setEditorOpen(false)\n },\n })),\n ]}\n activeTabId={hostTab?.id ?? activeHistoryId ?? LIST_TAB}\n onTabChange={(id) => {\n if (tabs?.some((tab) => tab.id === id)) {\n setHostTabId(id)\n return\n }\n setHostTabId(null)\n selectHistoryTab(id === LIST_TAB ? null : id)\n if (editorOpen) {\n const workflow = workflows.find((item) => item.external_id === id)\n if (workflow) setEditing(workflow)\n else setEditorOpen(false)\n }\n }}\n content={\n hostTab ? (\n hostTab.content\n ) : runsFor ? (\n <ConnectedWorkflowHistory\n inline\n hideHeader\n workflow={runsFor}\n onOpenChange={(open) => !open && openHistory(null)}\n formatDate={formatFriendlyDate}\n labels={historyLabels}\n renderErrorReport={\n renderErrorReport &&\n ((run) =>\n renderErrorReport(\n JSON.stringify(\n {\n workflow_ext_id: runsFor.external_id,\n run_id: run.id,\n status: run.status,\n error: run.error,\n },\n null,\n 2\n )\n ))\n }\n // Offered where the host shows files (its `openFiles`).\n onOpenResult={host.environment?.openFiles ? openResult : undefined}\n />\n ) : undefined\n }\n automationsOnly\n gridId=\"automations-grid-v1\"\n describeTrigger={(workflow) =>\n workflow.on_demand ? l.manualTrigger : describeTrigger(workflow)\n }\n formatDate={formatFriendlyDate}\n renderPerson={renderPerson}\n onViewRuns={showHistory}\n onOpenRow={edit}\n onEdit={edit}\n triggerChipLabels={WORKFLOWS_PAGE_TRIGGER_CHIP_LABELS}\n labels={listLabels}\n // A narrow row (a phone) keeps the actions' icons, so the host's tabs keep their room.\n header={(isCompact) => (\n <>\n {runsFor && !hostTab && (\n <Button\n variant=\"ghost\"\n onClick={() => edit(runsFor)}\n aria-label={l.editAria}\n title={isCompact ? l.edit : undefined}\n >\n <Pencil className={isCompact ? 'size-4' : 'mr-1.5 size-4'} />\n {!isCompact && l.edit}\n </Button>\n )}\n <Button\n onClick={() => openBuilder({ kind: 'new' })}\n aria-label={isCompact ? l.newWorkflow : undefined}\n title={isCompact ? l.newWorkflow : undefined}\n data-testid=\"new-workflow-button\"\n >\n <Plus className={isCompact ? 'size-4' : 'mr-1.5 size-4'} />\n {!isCompact && l.newWorkflow}\n </Button>\n </>\n )}\n />\n <ConnectedTaskComposer\n kind=\"workflow\"\n saveOnly\n startLater\n open={editorOpen}\n onOpenChange={setEditorOpen}\n workflow={editing}\n />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/workflows/useWorkflowsPageState.ts"],
|
|
4
|
-
"sourcesContent": ["import { useCallback, useEffect, useRef, useState } from 'react'\nimport { useWorkflows, type WorkflowResponse } from '@arbidocs/react'\n\n/** A saved workflow, a new one, or a host catalogue entry opened as a template. */\nexport type WorkflowBuilderSelection =\n { kind: 'saved'; id: string }
|
|
5
|
-
"mappings": ";AAAA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAS,oBAA2C;
|
|
4
|
+
"sourcesContent": ["import { useCallback, useEffect, useRef, useState } from 'react'\nimport { useWorkflows, type WorkflowResponse } from '@arbidocs/react'\n\n/** A saved workflow, a new one, or a host catalogue entry opened as a template. */\nexport type WorkflowBuilderSelection =\n | { kind: 'saved'; id: string }\n | { kind: 'new'; preset?: 'schedule' }\n | { kind: 'template'; id: string }\n\nexport interface WorkflowsPageStateOptions {\n initialBuilder?: WorkflowBuilderSelection\n /** A host deep link to consume once the saved workflow has loaded. */\n historyWorkflowId?: string | null\n onHistoryWorkflowConsumed?: () => void\n}\n\n/** Shared selection lifecycle for workflow tables, builders and history views. No routing or layout. */\nexport function useWorkflowsPageState({\n initialBuilder,\n historyWorkflowId,\n onHistoryWorkflowConsumed,\n}: WorkflowsPageStateOptions = {}) {\n const { data: workflows = [] } = useWorkflows({ live: true })\n const [builder, setBuilder] = useState<WorkflowBuilderSelection | null>(initialBuilder ?? null)\n const [builderKey, setBuilderKey] = useState(0)\n const [historyIds, setHistoryIds] = useState<string[]>([])\n const [activeHistoryId, setActiveHistoryId] = useState<string | null>(null)\n // Resolve against current query data: renaming/deleting a workflow cannot leave a stale dialog.\n const historyFor = workflows.find((workflow) => workflow.external_id === activeHistoryId) ?? null\n const historyTabs = historyIds.flatMap((id) => {\n const workflow = workflows.find((item) => item.external_id === id)\n return workflow ? [workflow] : []\n })\n\n const openHistory = useCallback((workflow: WorkflowResponse | null) => {\n const id = workflow?.external_id ?? null\n if (id) setHistoryIds((ids) => (ids.includes(id) ? ids : [...ids, id]))\n setActiveHistoryId(id)\n }, [])\n const closeHistoryTab = useCallback((id: string) => {\n setHistoryIds((ids) => ids.filter((item) => item !== id))\n setActiveHistoryId((active) => (active === id ? null : active))\n }, [])\n const builderKeyRef = useRef(builderKey)\n builderKeyRef.current = builderKey\n const openBuilder = useCallback((selection: WorkflowBuilderSelection) => {\n setBuilder(selection)\n // Reopening a template/new/saved selection resets its unsaved canvas; saving does not.\n setBuilderKey((key) => key + 1)\n }, [])\n /**\n * A save finished (it lands once the workflows are re-read): the builder it came from (its\n * `builderKey`, default the current one) now edits the saved workflow. A save the person left\n * meanwhile (Back, or another workflow opened) changes nothing: it never reopens the builder.\n */\n const builderSaved = useCallback((id: string, fromKey?: number) => {\n if (fromKey !== undefined && fromKey !== builderKeyRef.current) return\n setBuilder((open) => (open ? { kind: 'saved', id } : null))\n }, [])\n\n useEffect(() => {\n const workflow = workflows.find((item) => item.external_id === historyWorkflowId)\n if (!workflow) return\n openHistory(workflow)\n onHistoryWorkflowConsumed?.()\n }, [historyWorkflowId, onHistoryWorkflowConsumed, openHistory, workflows])\n\n return {\n workflows,\n builder,\n builderKey,\n openBuilder,\n closeBuilder: () => setBuilder(null),\n builderSaved,\n historyFor,\n historyTabs,\n activeHistoryId,\n openHistory,\n closeHistoryTab,\n selectHistoryTab: setActiveHistoryId,\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,aAAa,WAAW,QAAQ,gBAAgB;AACzD,SAAS,oBAA2C;AAgB7C,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,IAA+B,CAAC,GAAG;AACjC,QAAM,EAAE,MAAM,YAAY,CAAC,EAAE,IAAI,aAAa,EAAE,MAAM,KAAK,CAAC;AAC5D,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0C,kBAAkB,IAAI;AAC9F,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,CAAC;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAmB,CAAC,CAAC;AACzD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAwB,IAAI;AAE1E,QAAM,aAAa,UAAU,KAAK,CAAC,aAAa,SAAS,gBAAgB,eAAe,KAAK;AAC7F,QAAM,cAAc,WAAW,QAAQ,CAAC,OAAO;AAC7C,UAAM,WAAW,UAAU,KAAK,CAAC,SAAS,KAAK,gBAAgB,EAAE;AACjE,WAAO,WAAW,CAAC,QAAQ,IAAI,CAAC;AAAA,EAClC,CAAC;AAED,QAAM,cAAc,YAAY,CAAC,aAAsC;AACrE,UAAM,KAAK,UAAU,eAAe;AACpC,QAAI,GAAI,eAAc,CAAC,QAAS,IAAI,SAAS,EAAE,IAAI,MAAM,CAAC,GAAG,KAAK,EAAE,CAAE;AACtE,uBAAmB,EAAE;AAAA,EACvB,GAAG,CAAC,CAAC;AACL,QAAM,kBAAkB,YAAY,CAAC,OAAe;AAClD,kBAAc,CAAC,QAAQ,IAAI,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC;AACxD,uBAAmB,CAAC,WAAY,WAAW,KAAK,OAAO,MAAO;AAAA,EAChE,GAAG,CAAC,CAAC;AACL,QAAM,gBAAgB,OAAO,UAAU;AACvC,gBAAc,UAAU;AACxB,QAAM,cAAc,YAAY,CAAC,cAAwC;AACvE,eAAW,SAAS;AAEpB,kBAAc,CAAC,QAAQ,MAAM,CAAC;AAAA,EAChC,GAAG,CAAC,CAAC;AAML,QAAM,eAAe,YAAY,CAAC,IAAY,YAAqB;AACjE,QAAI,YAAY,UAAa,YAAY,cAAc,QAAS;AAChE,eAAW,CAAC,SAAU,OAAO,EAAE,MAAM,SAAS,GAAG,IAAI,IAAK;AAAA,EAC5D,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,UAAM,WAAW,UAAU,KAAK,CAAC,SAAS,KAAK,gBAAgB,iBAAiB;AAChF,QAAI,CAAC,SAAU;AACf,gBAAY,QAAQ;AACpB,gCAA4B;AAAA,EAC9B,GAAG,CAAC,mBAAmB,2BAA2B,aAAa,SAAS,CAAC;AAEzE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,MAAM,WAAW,IAAI;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,EACpB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var chunk4EK7TAP7_cjs = require('../chunk-4EK7TAP7.cjs');
|
|
5
5
|
var chunkLVIQWRC5_cjs = require('../chunk-LVIQWRC5.cjs');
|
|
6
|
-
var
|
|
6
|
+
var chunkUZHWXKS6_cjs = require('../chunk-UZHWXKS6.cjs');
|
|
7
7
|
var chunkPBPO54KZ_cjs = require('../chunk-PBPO54KZ.cjs');
|
|
8
|
-
require('../chunk-
|
|
8
|
+
require('../chunk-ULPTSXXO.cjs');
|
|
9
9
|
require('../chunk-ZU2B2RZK.cjs');
|
|
10
10
|
var chunkNSD37OF5_cjs = require('../chunk-NSD37OF5.cjs');
|
|
11
11
|
var chunkOEJQFPWO_cjs = require('../chunk-OEJQFPWO.cjs');
|
|
12
|
-
require('../chunk-
|
|
13
|
-
var chunkZW6FPIFC_cjs = require('../chunk-ZW6FPIFC.cjs');
|
|
12
|
+
require('../chunk-JV5TVUOU.cjs');
|
|
14
13
|
require('../chunk-IPSHAMO2.cjs');
|
|
15
|
-
var
|
|
14
|
+
var chunkZW6FPIFC_cjs = require('../chunk-ZW6FPIFC.cjs');
|
|
15
|
+
var chunkTKHCIHE2_cjs = require('../chunk-TKHCIHE2.cjs');
|
|
16
16
|
require('../chunk-JDFBEGQO.cjs');
|
|
17
17
|
require('../chunk-ULWRNGGE.cjs');
|
|
18
18
|
require('../chunk-PLIF5CUV.cjs');
|
|
@@ -67,8 +67,8 @@ function PanelsGrid({
|
|
|
67
67
|
editing,
|
|
68
68
|
testId
|
|
69
69
|
}) {
|
|
70
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
-
|
|
70
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkUZHWXKS6_cjs.DashboardGrid, { testId: testId && `${testId}-grid`, children: spec.panels.map((panel, i) => /* @__PURE__ */ jsxRuntime.jsx(chunkUZHWXKS6_cjs.DashboardCell, { width: panel.width, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
+
chunkUZHWXKS6_cjs.DashboardPanel,
|
|
72
72
|
{
|
|
73
73
|
panel,
|
|
74
74
|
labels: panelLabels,
|
|
@@ -167,7 +167,7 @@ function ConnectedDashboardsPage({
|
|
|
167
167
|
testId = "dashboards"
|
|
168
168
|
}) {
|
|
169
169
|
const l = { ...DEFAULT_DASHBOARDS_PAGE_LABELS, ...labels };
|
|
170
|
-
const { confirm, notify } =
|
|
170
|
+
const { confirm, notify } = chunkTKHCIHE2_cjs.useBlocksEnvironment();
|
|
171
171
|
const workflowsOn = react.useFeature("workflows");
|
|
172
172
|
const templates = siteTemplates ?? chunkLVIQWRC5_cjs.templatesFor(chunkLVIQWRC5_cjs.DASHBOARD_TEMPLATES, { workflows: workflowsOn });
|
|
173
173
|
const store = react.useDashboardStore();
|
|
@@ -365,7 +365,7 @@ function ConnectedDashboardsPage({
|
|
|
365
365
|
/* @__PURE__ */ jsxRuntime.jsx(ui.DialogDescription, { children: l.editPanelDescription })
|
|
366
366
|
] }),
|
|
367
367
|
editingPanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
368
|
-
|
|
368
|
+
chunk4EK7TAP7_cjs.PanelEditor,
|
|
369
369
|
{
|
|
370
370
|
value: editingPanel.panel,
|
|
371
371
|
onChange: (panel) => setEditingPanel({ ...editingPanel, panel }),
|
|
@@ -527,11 +527,11 @@ function ConnectedDashboardsPage({
|
|
|
527
527
|
|
|
528
528
|
Object.defineProperty(exports, "DEFAULT_PANEL_EDITOR_LABELS", {
|
|
529
529
|
enumerable: true,
|
|
530
|
-
get: function () { return
|
|
530
|
+
get: function () { return chunk4EK7TAP7_cjs.DEFAULT_PANEL_EDITOR_LABELS; }
|
|
531
531
|
});
|
|
532
532
|
Object.defineProperty(exports, "PanelEditor", {
|
|
533
533
|
enumerable: true,
|
|
534
|
-
get: function () { return
|
|
534
|
+
get: function () { return chunk4EK7TAP7_cjs.PanelEditor; }
|
|
535
535
|
});
|
|
536
536
|
Object.defineProperty(exports, "DASHBOARD_TEMPLATES", {
|
|
537
537
|
enumerable: true,
|
|
@@ -555,47 +555,47 @@ Object.defineProperty(exports, "usagePanels", {
|
|
|
555
555
|
});
|
|
556
556
|
Object.defineProperty(exports, "ChartRenderer", {
|
|
557
557
|
enumerable: true,
|
|
558
|
-
get: function () { return
|
|
558
|
+
get: function () { return chunkUZHWXKS6_cjs.ChartRenderer; }
|
|
559
559
|
});
|
|
560
560
|
Object.defineProperty(exports, "ChartTable", {
|
|
561
561
|
enumerable: true,
|
|
562
|
-
get: function () { return
|
|
562
|
+
get: function () { return chunkUZHWXKS6_cjs.ChartTable; }
|
|
563
563
|
});
|
|
564
564
|
Object.defineProperty(exports, "DEFAULT_CHART_RENDERER_LABELS", {
|
|
565
565
|
enumerable: true,
|
|
566
|
-
get: function () { return
|
|
566
|
+
get: function () { return chunkUZHWXKS6_cjs.DEFAULT_CHART_RENDERER_LABELS; }
|
|
567
567
|
});
|
|
568
568
|
Object.defineProperty(exports, "DEFAULT_DASHBOARD_PANEL_LABELS", {
|
|
569
569
|
enumerable: true,
|
|
570
|
-
get: function () { return
|
|
570
|
+
get: function () { return chunkUZHWXKS6_cjs.DEFAULT_DASHBOARD_PANEL_LABELS; }
|
|
571
571
|
});
|
|
572
572
|
Object.defineProperty(exports, "DEFAULT_PANEL_VIEW_LABELS", {
|
|
573
573
|
enumerable: true,
|
|
574
|
-
get: function () { return
|
|
574
|
+
get: function () { return chunkUZHWXKS6_cjs.DEFAULT_PANEL_VIEW_LABELS; }
|
|
575
575
|
});
|
|
576
576
|
Object.defineProperty(exports, "DashboardCell", {
|
|
577
577
|
enumerable: true,
|
|
578
|
-
get: function () { return
|
|
578
|
+
get: function () { return chunkUZHWXKS6_cjs.DashboardCell; }
|
|
579
579
|
});
|
|
580
580
|
Object.defineProperty(exports, "DashboardGrid", {
|
|
581
581
|
enumerable: true,
|
|
582
|
-
get: function () { return
|
|
582
|
+
get: function () { return chunkUZHWXKS6_cjs.DashboardGrid; }
|
|
583
583
|
});
|
|
584
584
|
Object.defineProperty(exports, "DashboardPanel", {
|
|
585
585
|
enumerable: true,
|
|
586
|
-
get: function () { return
|
|
586
|
+
get: function () { return chunkUZHWXKS6_cjs.DashboardPanel; }
|
|
587
587
|
});
|
|
588
588
|
Object.defineProperty(exports, "PANEL_FORMATTERS", {
|
|
589
589
|
enumerable: true,
|
|
590
|
-
get: function () { return
|
|
590
|
+
get: function () { return chunkUZHWXKS6_cjs.PANEL_FORMATTERS; }
|
|
591
591
|
});
|
|
592
592
|
Object.defineProperty(exports, "PanelView", {
|
|
593
593
|
enumerable: true,
|
|
594
|
-
get: function () { return
|
|
594
|
+
get: function () { return chunkUZHWXKS6_cjs.PanelView; }
|
|
595
595
|
});
|
|
596
596
|
Object.defineProperty(exports, "clampPanelWidth", {
|
|
597
597
|
enumerable: true,
|
|
598
|
-
get: function () { return
|
|
598
|
+
get: function () { return chunkUZHWXKS6_cjs.clampPanelWidth; }
|
|
599
599
|
});
|
|
600
600
|
Object.defineProperty(exports, "DEFAULT_UNIT_FORMATTERS", {
|
|
601
601
|
enumerable: true,
|