@david-xpn/llm-ui-feedback 0.1.13 → 0.1.15
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/index.js +219 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +227 -30
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ function getContrastColor(hexColor) {
|
|
|
52
52
|
|
|
53
53
|
// src/components/FloatingButton.tsx
|
|
54
54
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
55
|
-
function FloatingButton({ onPickClick, onPanelToggle, draftCount, panelOpen, position, buttonColor }) {
|
|
55
|
+
function FloatingButton({ onPickClick, onPanelToggle, draftCount, panelOpen, position, buttonColor, authenticated }) {
|
|
56
56
|
const isBottom = position.includes("bottom");
|
|
57
57
|
const isRight = position.includes("right");
|
|
58
58
|
const anchor = {
|
|
@@ -66,7 +66,7 @@ function FloatingButton({ onPickClick, onPanelToggle, draftCount, panelOpen, pos
|
|
|
66
66
|
gap: 8
|
|
67
67
|
};
|
|
68
68
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: anchor, children: [
|
|
69
|
-
|
|
69
|
+
authenticated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
70
70
|
"button",
|
|
71
71
|
{
|
|
72
72
|
onClick: onPanelToggle,
|
|
@@ -96,7 +96,7 @@ function FloatingButton({ onPickClick, onPanelToggle, draftCount, panelOpen, pos
|
|
|
96
96
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "13", width: "2", height: "2", rx: "0.5", fill: "currentColor" }),
|
|
97
97
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6", y: "13", width: "10", height: "2", rx: "0.5", fill: "currentColor" })
|
|
98
98
|
] }),
|
|
99
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
99
|
+
draftCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
100
100
|
"span",
|
|
101
101
|
{
|
|
102
102
|
style: {
|
|
@@ -549,7 +549,7 @@ function SidePanel({
|
|
|
549
549
|
"Feedback Session",
|
|
550
550
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { fontSize: 11, fontWeight: 400, color: "#9ca3af", marginLeft: 6 }, children: [
|
|
551
551
|
"v",
|
|
552
|
-
"0.1.
|
|
552
|
+
"0.1.15"
|
|
553
553
|
] })
|
|
554
554
|
] }),
|
|
555
555
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -971,11 +971,59 @@ Comment: ${comment.trim()}`] : []
|
|
|
971
971
|
// src/components/SubmitModal.tsx
|
|
972
972
|
var import_react5 = require("react");
|
|
973
973
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
974
|
-
function SubmitModal({ count, onSubmit, onCancel, onDone, submitting, submittedUrl }) {
|
|
974
|
+
function SubmitModal({ count, onSubmit, onCancel, onDone, submitting, submittedUrl, api }) {
|
|
975
975
|
const [title, setTitle] = (0, import_react5.useState)("");
|
|
976
976
|
const [copied, setCopied] = (0, import_react5.useState)(false);
|
|
977
|
+
const [linearConfigured, setLinearConfigured] = (0, import_react5.useState)(null);
|
|
978
|
+
const [linearLoading, setLinearLoading] = (0, import_react5.useState)(true);
|
|
979
|
+
const [createTicket, setCreateTicket] = (0, import_react5.useState)(true);
|
|
980
|
+
const [ticketTitle, setTicketTitle] = (0, import_react5.useState)("");
|
|
981
|
+
const [ticketTitleEdited, setTicketTitleEdited] = (0, import_react5.useState)(false);
|
|
982
|
+
const [projects, setProjects] = (0, import_react5.useState)([]);
|
|
983
|
+
const [labels, setLabels] = (0, import_react5.useState)([]);
|
|
984
|
+
const [selectedProjectId, setSelectedProjectId] = (0, import_react5.useState)("");
|
|
985
|
+
const [selectedLabelIds, setSelectedLabelIds] = (0, import_react5.useState)([]);
|
|
986
|
+
const [linearError, setLinearError] = (0, import_react5.useState)(null);
|
|
987
|
+
(0, import_react5.useEffect)(() => {
|
|
988
|
+
let cancelled = false;
|
|
989
|
+
setLinearLoading(true);
|
|
990
|
+
api.getLinearStatus().then(({ configured }) => {
|
|
991
|
+
if (cancelled) return;
|
|
992
|
+
setLinearConfigured(configured);
|
|
993
|
+
if (!configured) {
|
|
994
|
+
setLinearLoading(false);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
return Promise.all([api.fetchLinearProjects(), api.fetchLinearLabels()]).then(([p, l]) => {
|
|
998
|
+
if (cancelled) return;
|
|
999
|
+
setProjects(p);
|
|
1000
|
+
setLabels(l);
|
|
1001
|
+
if (p.length > 0) setSelectedProjectId(p[0].id);
|
|
1002
|
+
setLinearLoading(false);
|
|
1003
|
+
});
|
|
1004
|
+
}).catch((err) => {
|
|
1005
|
+
if (!cancelled) {
|
|
1006
|
+
setLinearConfigured(false);
|
|
1007
|
+
setLinearError(err.message);
|
|
1008
|
+
setLinearLoading(false);
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
1011
|
+
return () => {
|
|
1012
|
+
cancelled = true;
|
|
1013
|
+
};
|
|
1014
|
+
}, [api]);
|
|
1015
|
+
(0, import_react5.useEffect)(() => {
|
|
1016
|
+
if (!ticketTitleEdited) setTicketTitle(title);
|
|
1017
|
+
}, [title, ticketTitleEdited]);
|
|
977
1018
|
const handleSubmit = () => {
|
|
978
|
-
if (title.trim()
|
|
1019
|
+
if (!title.trim() || submitting) return;
|
|
1020
|
+
if (createTicket && linearConfigured && ticketTitle.trim()) {
|
|
1021
|
+
onSubmit(title.trim(), {
|
|
1022
|
+
title: ticketTitle.trim(),
|
|
1023
|
+
...selectedProjectId ? { projectId: selectedProjectId } : {},
|
|
1024
|
+
...selectedLabelIds.length ? { labelIds: selectedLabelIds } : {}
|
|
1025
|
+
});
|
|
1026
|
+
} else {
|
|
979
1027
|
onSubmit(title.trim());
|
|
980
1028
|
}
|
|
981
1029
|
};
|
|
@@ -988,6 +1036,18 @@ function SubmitModal({ count, onSubmit, onCancel, onDone, submitting, submittedU
|
|
|
988
1036
|
} catch {
|
|
989
1037
|
}
|
|
990
1038
|
};
|
|
1039
|
+
const toggleLabel = (id) => {
|
|
1040
|
+
setSelectedLabelIds((prev) => prev.includes(id) ? prev.filter((l) => l !== id) : [...prev, id]);
|
|
1041
|
+
};
|
|
1042
|
+
const inputStyle = {
|
|
1043
|
+
width: "100%",
|
|
1044
|
+
padding: 10,
|
|
1045
|
+
borderRadius: 8,
|
|
1046
|
+
border: "1px solid #d1d5db",
|
|
1047
|
+
fontSize: 14,
|
|
1048
|
+
boxSizing: "border-box",
|
|
1049
|
+
background: "#fff"
|
|
1050
|
+
};
|
|
991
1051
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
992
1052
|
"div",
|
|
993
1053
|
{
|
|
@@ -1014,8 +1074,10 @@ function SubmitModal({ count, onSubmit, onCancel, onDone, submitting, submittedU
|
|
|
1014
1074
|
background: "#fff",
|
|
1015
1075
|
borderRadius: 12,
|
|
1016
1076
|
padding: 24,
|
|
1017
|
-
width:
|
|
1018
|
-
maxWidth: "
|
|
1077
|
+
width: 460,
|
|
1078
|
+
maxWidth: "92vw",
|
|
1079
|
+
maxHeight: "90vh",
|
|
1080
|
+
overflowY: "auto",
|
|
1019
1081
|
boxShadow: "0 8px 32px rgba(0,0,0,0.2)"
|
|
1020
1082
|
},
|
|
1021
1083
|
children: submittedUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
@@ -1077,7 +1139,7 @@ function SubmitModal({ count, onSubmit, onCancel, onDone, submitting, submittedU
|
|
|
1077
1139
|
) })
|
|
1078
1140
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1079
1141
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { style: { margin: "0 0 16px", fontSize: 16 }, children: "Submit Feedback" }),
|
|
1080
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: { margin: "0 0
|
|
1142
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: { margin: "0 0 8px", fontSize: 13, color: "#6b7280" }, children: [
|
|
1081
1143
|
count,
|
|
1082
1144
|
" item",
|
|
1083
1145
|
count !== 1 ? "s" : "",
|
|
@@ -1091,20 +1153,138 @@ function SubmitModal({ count, onSubmit, onCancel, onDone, submitting, submittedU
|
|
|
1091
1153
|
onChange: (e) => setTitle(e.target.value),
|
|
1092
1154
|
placeholder: "e.g. Homepage redesign feedback",
|
|
1093
1155
|
autoFocus: true,
|
|
1094
|
-
|
|
1095
|
-
|
|
1156
|
+
style: inputStyle
|
|
1157
|
+
}
|
|
1158
|
+
),
|
|
1159
|
+
linearLoading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1160
|
+
"div",
|
|
1161
|
+
{
|
|
1162
|
+
style: {
|
|
1163
|
+
marginTop: 16,
|
|
1164
|
+
padding: 12,
|
|
1165
|
+
borderRadius: 8,
|
|
1166
|
+
border: "1px solid #e5e7eb",
|
|
1167
|
+
background: "#f9fafb",
|
|
1168
|
+
display: "flex",
|
|
1169
|
+
alignItems: "center",
|
|
1170
|
+
gap: 10,
|
|
1171
|
+
fontSize: 13,
|
|
1172
|
+
color: "#6b7280"
|
|
1096
1173
|
},
|
|
1174
|
+
children: [
|
|
1175
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `@keyframes llmuif-spin { to { transform: rotate(360deg); } }` }),
|
|
1176
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1177
|
+
"span",
|
|
1178
|
+
{
|
|
1179
|
+
style: {
|
|
1180
|
+
display: "inline-block",
|
|
1181
|
+
width: 14,
|
|
1182
|
+
height: 14,
|
|
1183
|
+
border: "2px solid #d1d5db",
|
|
1184
|
+
borderTopColor: "#3b82f6",
|
|
1185
|
+
borderRadius: "50%",
|
|
1186
|
+
animation: "llmuif-spin 0.8s linear infinite"
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
),
|
|
1190
|
+
"Loading Linear projects and labels\u2026"
|
|
1191
|
+
]
|
|
1192
|
+
}
|
|
1193
|
+
),
|
|
1194
|
+
!linearLoading && linearConfigured && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1195
|
+
"div",
|
|
1196
|
+
{
|
|
1097
1197
|
style: {
|
|
1098
|
-
|
|
1099
|
-
padding:
|
|
1198
|
+
marginTop: 16,
|
|
1199
|
+
padding: 12,
|
|
1100
1200
|
borderRadius: 8,
|
|
1101
|
-
border: "1px solid #
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1201
|
+
border: "1px solid #e5e7eb",
|
|
1202
|
+
background: "#f9fafb"
|
|
1203
|
+
},
|
|
1204
|
+
children: [
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 13, fontWeight: 500, color: "#111827" }, children: [
|
|
1206
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1207
|
+
"input",
|
|
1208
|
+
{
|
|
1209
|
+
type: "checkbox",
|
|
1210
|
+
checked: createTicket,
|
|
1211
|
+
onChange: (e) => setCreateTicket(e.target.checked)
|
|
1212
|
+
}
|
|
1213
|
+
),
|
|
1214
|
+
"Also create a Linear ticket"
|
|
1215
|
+
] }),
|
|
1216
|
+
createTicket && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 12, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1218
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { style: { display: "block", fontSize: 12, color: "#6b7280", marginBottom: 4 }, children: "Ticket title" }),
|
|
1219
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1220
|
+
"input",
|
|
1221
|
+
{
|
|
1222
|
+
type: "text",
|
|
1223
|
+
value: ticketTitle,
|
|
1224
|
+
onChange: (e) => {
|
|
1225
|
+
setTicketTitleEdited(true);
|
|
1226
|
+
setTicketTitle(e.target.value);
|
|
1227
|
+
},
|
|
1228
|
+
placeholder: "Linear ticket title",
|
|
1229
|
+
style: inputStyle
|
|
1230
|
+
}
|
|
1231
|
+
)
|
|
1232
|
+
] }),
|
|
1233
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1234
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { style: { display: "block", fontSize: 12, color: "#6b7280", marginBottom: 4 }, children: "Project" }),
|
|
1235
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1236
|
+
"select",
|
|
1237
|
+
{
|
|
1238
|
+
value: selectedProjectId,
|
|
1239
|
+
onChange: (e) => setSelectedProjectId(e.target.value),
|
|
1240
|
+
style: { ...inputStyle, appearance: "auto" },
|
|
1241
|
+
children: [
|
|
1242
|
+
projects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "(no projects)" }),
|
|
1243
|
+
projects.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("option", { value: p.id, children: [
|
|
1244
|
+
p.name,
|
|
1245
|
+
p.teams[0] ? ` \u2014 ${p.teams[0].key}` : ""
|
|
1246
|
+
] }, p.id))
|
|
1247
|
+
]
|
|
1248
|
+
}
|
|
1249
|
+
)
|
|
1250
|
+
] }),
|
|
1251
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { style: { display: "block", fontSize: 12, color: "#6b7280", marginBottom: 4 }, children: "Labels" }),
|
|
1253
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: [
|
|
1254
|
+
labels.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 12, color: "#9ca3af" }, children: "(no labels)" }),
|
|
1255
|
+
labels.map((l) => {
|
|
1256
|
+
const active = selectedLabelIds.includes(l.id);
|
|
1257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1258
|
+
"button",
|
|
1259
|
+
{
|
|
1260
|
+
type: "button",
|
|
1261
|
+
onClick: () => toggleLabel(l.id),
|
|
1262
|
+
style: {
|
|
1263
|
+
padding: "4px 10px",
|
|
1264
|
+
borderRadius: 999,
|
|
1265
|
+
border: `1px solid ${active ? l.color : "#d1d5db"}`,
|
|
1266
|
+
background: active ? l.color : "#fff",
|
|
1267
|
+
color: active ? "#fff" : "#374151",
|
|
1268
|
+
fontSize: 12,
|
|
1269
|
+
cursor: "pointer"
|
|
1270
|
+
},
|
|
1271
|
+
children: l.name
|
|
1272
|
+
},
|
|
1273
|
+
l.id
|
|
1274
|
+
);
|
|
1275
|
+
})
|
|
1276
|
+
] })
|
|
1277
|
+
] })
|
|
1278
|
+
] })
|
|
1279
|
+
]
|
|
1105
1280
|
}
|
|
1106
1281
|
),
|
|
1107
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1282
|
+
!linearLoading && linearConfigured === false && !linearError && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { marginTop: 12, fontSize: 12, color: "#9ca3af" }, children: "Linear integration is not configured. Add a Linear token in the viewer Settings page to also create tickets from here." }),
|
|
1283
|
+
linearError && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: { marginTop: 12, fontSize: 12, color: "#b91c1c" }, children: [
|
|
1284
|
+
"Linear: ",
|
|
1285
|
+
linearError
|
|
1286
|
+
] }),
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 20, justifyContent: "flex-end" }, children: [
|
|
1108
1288
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1109
1289
|
"button",
|
|
1110
1290
|
{
|
|
@@ -1396,6 +1576,17 @@ function createApiClient(apiUrl, clientId) {
|
|
|
1396
1576
|
body: JSON.stringify(data)
|
|
1397
1577
|
});
|
|
1398
1578
|
},
|
|
1579
|
+
async getLinearStatus() {
|
|
1580
|
+
return apiFetch("/linear/status");
|
|
1581
|
+
},
|
|
1582
|
+
async fetchLinearProjects() {
|
|
1583
|
+
const data = await apiFetch("/linear/projects");
|
|
1584
|
+
return data.items;
|
|
1585
|
+
},
|
|
1586
|
+
async fetchLinearLabels() {
|
|
1587
|
+
const data = await apiFetch("/linear/labels");
|
|
1588
|
+
return data.items;
|
|
1589
|
+
},
|
|
1399
1590
|
async getUploadUrl(entryId, timestamp) {
|
|
1400
1591
|
return apiFetch("/upload-url", {
|
|
1401
1592
|
method: "POST",
|
|
@@ -1674,12 +1865,16 @@ function FeedbackWidget({
|
|
|
1674
1865
|
}
|
|
1675
1866
|
}, [api]);
|
|
1676
1867
|
const submittedDraftIdsRef = (0, import_react7.useRef)([]);
|
|
1677
|
-
const handleSubmit = (0, import_react7.useCallback)(async (title) => {
|
|
1868
|
+
const handleSubmit = (0, import_react7.useCallback)(async (title, linearTicket) => {
|
|
1678
1869
|
dispatch({ type: "SET_SUBMITTING", submitting: true });
|
|
1679
1870
|
const draftIds = Array.from(state.selectedDraftIds);
|
|
1680
1871
|
submittedDraftIdsRef.current = draftIds;
|
|
1681
1872
|
try {
|
|
1682
|
-
const result = await api.submitSession({
|
|
1873
|
+
const result = await api.submitSession({
|
|
1874
|
+
title,
|
|
1875
|
+
draftIds,
|
|
1876
|
+
...linearTicket ? { linearTicket } : {}
|
|
1877
|
+
});
|
|
1683
1878
|
const group = result.feedbackGroup;
|
|
1684
1879
|
const shareUrl = group.shareToken ? `${apiUrl}/feedback-groups/${group.id}/markdown?token=${group.shareToken}` : "";
|
|
1685
1880
|
dispatch({ type: "SUBMIT_SUCCESS", shareUrl });
|
|
@@ -1702,7 +1897,8 @@ function FeedbackWidget({
|
|
|
1702
1897
|
draftCount: state.drafts.length,
|
|
1703
1898
|
panelOpen: state.panelOpen,
|
|
1704
1899
|
position,
|
|
1705
|
-
buttonColor
|
|
1900
|
+
buttonColor,
|
|
1901
|
+
authenticated: session.status === "authenticated"
|
|
1706
1902
|
}
|
|
1707
1903
|
),
|
|
1708
1904
|
state.picking && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PickOverlay, { onPick: handlePick, onCancel: handleCancelPicking }),
|
|
@@ -1740,7 +1936,8 @@ function FeedbackWidget({
|
|
|
1740
1936
|
onCancel: () => dispatch({ type: "CLOSE_SUBMIT_MODAL" }),
|
|
1741
1937
|
onDone: handleSubmitDone,
|
|
1742
1938
|
submitting: state.submitting,
|
|
1743
|
-
submittedUrl: state.submittedShareUrl
|
|
1939
|
+
submittedUrl: state.submittedShareUrl,
|
|
1940
|
+
api
|
|
1744
1941
|
}
|
|
1745
1942
|
)
|
|
1746
1943
|
] }),
|