@david-xpn/llm-ui-feedback 0.1.1 → 0.1.3
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 +131 -66
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -74
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/FeedbackWidget.tsx
|
|
2
|
-
import { useReducer, useCallback as useCallback3, useEffect as
|
|
2
|
+
import { useReducer, useCallback as useCallback3, useEffect as useEffect4, useMemo, useRef as useRef3 } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
|
|
5
5
|
// src/utils/color.ts
|
|
@@ -89,7 +89,7 @@ function FloatingButton({ onPickClick, onPanelToggle, draftCount, panelOpen, pos
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// src/components/PickOverlay.tsx
|
|
92
|
-
import {
|
|
92
|
+
import { useEffect, useRef, useState } from "react";
|
|
93
93
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
94
94
|
var WIDGET_CONTAINER_ID = "llm-ui-feedback-root";
|
|
95
95
|
function getElementsBeneathRect(rect) {
|
|
@@ -100,9 +100,7 @@ function getElementsBeneathRect(rect) {
|
|
|
100
100
|
for (let x = rect.x; x <= rect.x + rect.width; x += step) {
|
|
101
101
|
for (let y = rect.y; y <= rect.y + rect.height; y += step) {
|
|
102
102
|
const el = document.elementFromPoint(x, y);
|
|
103
|
-
if (el && !seen.has(el))
|
|
104
|
-
seen.add(el);
|
|
105
|
-
}
|
|
103
|
+
if (el && !seen.has(el)) seen.add(el);
|
|
106
104
|
}
|
|
107
105
|
}
|
|
108
106
|
for (const [x, y] of [
|
|
@@ -118,55 +116,95 @@ function getElementsBeneathRect(rect) {
|
|
|
118
116
|
if (container) container.style.display = "";
|
|
119
117
|
return Array.from(seen).filter((el) => !container?.contains(el));
|
|
120
118
|
}
|
|
119
|
+
function getElementAtPoint(x, y) {
|
|
120
|
+
const container = document.getElementById(WIDGET_CONTAINER_ID);
|
|
121
|
+
if (container) container.style.display = "none";
|
|
122
|
+
const el = document.elementFromPoint(x, y);
|
|
123
|
+
if (container) container.style.display = "";
|
|
124
|
+
if (el && container?.contains(el)) return null;
|
|
125
|
+
return el;
|
|
126
|
+
}
|
|
127
|
+
var DRAG_THRESHOLD = 10;
|
|
121
128
|
function PickOverlay({ onPick, onCancel }) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
129
|
+
const activeRef = useRef(false);
|
|
130
|
+
const startRef = useRef(null);
|
|
131
|
+
const isDragRef = useRef(false);
|
|
132
|
+
const [hoverRect, setHoverRect] = useState(null);
|
|
133
|
+
const [selRect, setSelRect] = useState(null);
|
|
134
|
+
const onPickRef = useRef(onPick);
|
|
135
|
+
onPickRef.current = onPick;
|
|
136
|
+
const onCancelRef = useRef(onCancel);
|
|
137
|
+
onCancelRef.current = onCancel;
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
function handleMouseMove(e) {
|
|
140
|
+
if (activeRef.current && startRef.current) {
|
|
141
|
+
const dx = Math.abs(e.clientX - startRef.current.x);
|
|
142
|
+
const dy = Math.abs(e.clientY - startRef.current.y);
|
|
143
|
+
if (dx >= DRAG_THRESHOLD || dy >= DRAG_THRESHOLD) {
|
|
144
|
+
isDragRef.current = true;
|
|
145
|
+
}
|
|
146
|
+
if (isDragRef.current) {
|
|
147
|
+
setHoverRect(null);
|
|
148
|
+
setSelRect({
|
|
149
|
+
left: Math.min(startRef.current.x, e.clientX),
|
|
150
|
+
top: Math.min(startRef.current.y, e.clientY),
|
|
151
|
+
width: Math.abs(e.clientX - startRef.current.x),
|
|
152
|
+
height: Math.abs(e.clientY - startRef.current.y)
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
} else {
|
|
157
|
+
const el = getElementAtPoint(e.clientX, e.clientY);
|
|
158
|
+
if (el) {
|
|
159
|
+
const r = el.getBoundingClientRect();
|
|
160
|
+
setHoverRect({ x: r.left, y: r.top, width: r.width, height: r.height });
|
|
161
|
+
} else {
|
|
162
|
+
setHoverRect(null);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
154
165
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
function handleMouseDown(e) {
|
|
167
|
+
if (e.button !== 0) return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
e.stopPropagation();
|
|
170
|
+
startRef.current = { x: e.clientX, y: e.clientY };
|
|
171
|
+
isDragRef.current = false;
|
|
172
|
+
activeRef.current = true;
|
|
173
|
+
}
|
|
174
|
+
function handleMouseUp(e) {
|
|
175
|
+
if (!activeRef.current || !startRef.current) return;
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
e.stopPropagation();
|
|
178
|
+
const start = startRef.current;
|
|
179
|
+
activeRef.current = false;
|
|
180
|
+
startRef.current = null;
|
|
181
|
+
setSelRect(null);
|
|
182
|
+
setHoverRect(null);
|
|
183
|
+
if (!isDragRef.current) {
|
|
184
|
+
const el = getElementAtPoint(e.clientX, e.clientY);
|
|
185
|
+
if (el) {
|
|
186
|
+
const r = el.getBoundingClientRect();
|
|
187
|
+
onPickRef.current({ x: r.left, y: r.top, width: r.width, height: r.height }, [el]);
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const x = Math.min(start.x, e.clientX);
|
|
192
|
+
const y = Math.min(start.y, e.clientY);
|
|
193
|
+
const width = Math.abs(e.clientX - start.x);
|
|
194
|
+
const height = Math.abs(e.clientY - start.y);
|
|
195
|
+
const rect = { x, y, width, height };
|
|
196
|
+
const elements = getElementsBeneathRect(rect);
|
|
197
|
+
onPickRef.current(rect, elements);
|
|
198
|
+
}
|
|
199
|
+
function handleKeyDown(e) {
|
|
200
|
+
if (e.key === "Escape") {
|
|
201
|
+
activeRef.current = false;
|
|
202
|
+
startRef.current = null;
|
|
203
|
+
setSelRect(null);
|
|
204
|
+
setHoverRect(null);
|
|
205
|
+
onCancelRef.current();
|
|
206
|
+
}
|
|
167
207
|
}
|
|
168
|
-
}, [onCancel]);
|
|
169
|
-
useEffect(() => {
|
|
170
208
|
document.addEventListener("mousedown", handleMouseDown, true);
|
|
171
209
|
document.addEventListener("mousemove", handleMouseMove, true);
|
|
172
210
|
document.addEventListener("mouseup", handleMouseUp, true);
|
|
@@ -177,21 +215,11 @@ function PickOverlay({ onPick, onCancel }) {
|
|
|
177
215
|
document.removeEventListener("mouseup", handleMouseUp, true);
|
|
178
216
|
document.removeEventListener("keydown", handleKeyDown, true);
|
|
179
217
|
};
|
|
180
|
-
}, [
|
|
181
|
-
let selRect = null;
|
|
182
|
-
if (start && current && dragging) {
|
|
183
|
-
selRect = {
|
|
184
|
-
left: Math.min(start.x, current.x),
|
|
185
|
-
top: Math.min(start.y, current.y),
|
|
186
|
-
width: Math.abs(current.x - start.x),
|
|
187
|
-
height: Math.abs(current.y - start.y)
|
|
188
|
-
};
|
|
189
|
-
}
|
|
218
|
+
}, []);
|
|
190
219
|
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
191
220
|
/* @__PURE__ */ jsx2(
|
|
192
221
|
"div",
|
|
193
222
|
{
|
|
194
|
-
ref: overlayRef,
|
|
195
223
|
style: {
|
|
196
224
|
position: "fixed",
|
|
197
225
|
top: 0,
|
|
@@ -221,12 +249,30 @@ function PickOverlay({ onPick, onCancel }) {
|
|
|
221
249
|
pointerEvents: "none"
|
|
222
250
|
},
|
|
223
251
|
children: [
|
|
224
|
-
"
|
|
252
|
+
"Click an element or drag to select an area. Press ",
|
|
225
253
|
/* @__PURE__ */ jsx2("strong", { children: "Esc" }),
|
|
226
254
|
" to cancel."
|
|
227
255
|
]
|
|
228
256
|
}
|
|
229
257
|
),
|
|
258
|
+
hoverRect && /* @__PURE__ */ jsx2(
|
|
259
|
+
"div",
|
|
260
|
+
{
|
|
261
|
+
style: {
|
|
262
|
+
position: "fixed",
|
|
263
|
+
left: hoverRect.x,
|
|
264
|
+
top: hoverRect.y,
|
|
265
|
+
width: hoverRect.width,
|
|
266
|
+
height: hoverRect.height,
|
|
267
|
+
border: "2px solid #3b82f6",
|
|
268
|
+
background: "rgba(59, 130, 246, 0.08)",
|
|
269
|
+
zIndex: 99998,
|
|
270
|
+
pointerEvents: "none",
|
|
271
|
+
borderRadius: 2,
|
|
272
|
+
transition: "all 0.1s ease-out"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
),
|
|
230
276
|
selRect && /* @__PURE__ */ jsx2(
|
|
231
277
|
"div",
|
|
232
278
|
{
|
|
@@ -585,10 +631,27 @@ function SidePanel({
|
|
|
585
631
|
}
|
|
586
632
|
|
|
587
633
|
// src/components/DraftModal.tsx
|
|
588
|
-
import { useState as useState4 } from "react";
|
|
634
|
+
import { useState as useState4, useEffect as useEffect2 } from "react";
|
|
589
635
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
636
|
+
var WIDGET_CONTAINER_ID2 = "llm-ui-feedback-root";
|
|
590
637
|
function DraftModal({ pendingContext, addingDraft, onAdd, onCancel }) {
|
|
591
638
|
const [comment, setComment] = useState4("");
|
|
639
|
+
useEffect2(() => {
|
|
640
|
+
const container = document.getElementById(WIDGET_CONTAINER_ID2);
|
|
641
|
+
const inerted = [];
|
|
642
|
+
for (const child of Array.from(document.body.children)) {
|
|
643
|
+
if (child === container || child.id === WIDGET_CONTAINER_ID2) continue;
|
|
644
|
+
if (!child.hasAttribute("inert")) {
|
|
645
|
+
child.setAttribute("inert", "");
|
|
646
|
+
inerted.push(child);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return () => {
|
|
650
|
+
for (const el of inerted) {
|
|
651
|
+
el.removeAttribute("inert");
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
}, []);
|
|
592
655
|
const handleAdd = () => {
|
|
593
656
|
if (comment.trim() && !addingDraft) {
|
|
594
657
|
onAdd(comment.trim());
|
|
@@ -937,10 +1000,10 @@ function dataUrlToBlob(dataUrl) {
|
|
|
937
1000
|
}
|
|
938
1001
|
|
|
939
1002
|
// src/hooks/useSession.ts
|
|
940
|
-
import { useState as useState6, useEffect as
|
|
1003
|
+
import { useState as useState6, useEffect as useEffect3, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
941
1004
|
var SESSION_TOKEN_KEY = "llm_feedback_session_token";
|
|
942
1005
|
var USER_KEY = "llm_feedback_user";
|
|
943
|
-
var AUTH_BYPASS = typeof
|
|
1006
|
+
var AUTH_BYPASS = !!(typeof globalThis !== "undefined" && globalThis.__FEEDBACK_AUTH_BYPASS__);
|
|
944
1007
|
function getSessionToken() {
|
|
945
1008
|
if (AUTH_BYPASS) return "bypass-token";
|
|
946
1009
|
return localStorage.getItem(SESSION_TOKEN_KEY);
|
|
@@ -955,7 +1018,7 @@ function useSession(apiUrl, clientId) {
|
|
|
955
1018
|
const [status, setStatus] = useState6(AUTH_BYPASS ? "authenticated" : "loading");
|
|
956
1019
|
const [user, setUser] = useState6(AUTH_BYPASS ? BYPASS_USER : null);
|
|
957
1020
|
const pendingAuthRef = useRef2(false);
|
|
958
|
-
|
|
1021
|
+
useEffect3(() => {
|
|
959
1022
|
if (AUTH_BYPASS) return;
|
|
960
1023
|
let cancelled = false;
|
|
961
1024
|
async function checkSession() {
|
|
@@ -996,7 +1059,7 @@ function useSession(apiUrl, clientId) {
|
|
|
996
1059
|
cancelled = true;
|
|
997
1060
|
};
|
|
998
1061
|
}, [apiUrl]);
|
|
999
|
-
|
|
1062
|
+
useEffect3(() => {
|
|
1000
1063
|
function handleMessage(event) {
|
|
1001
1064
|
if (event.data?.type !== "feedback-auth") return;
|
|
1002
1065
|
const { sessionToken, user: userData } = event.data;
|
|
@@ -1229,13 +1292,13 @@ function FeedbackWidget({
|
|
|
1229
1292
|
() => createApiClient(apiUrl, clientId),
|
|
1230
1293
|
[apiUrl, clientId]
|
|
1231
1294
|
);
|
|
1232
|
-
|
|
1295
|
+
useEffect4(() => {
|
|
1233
1296
|
if (session.status === "authenticated" && pendingOpenRef.current) {
|
|
1234
1297
|
pendingOpenRef.current = false;
|
|
1235
1298
|
dispatch({ type: "OPEN_PANEL" });
|
|
1236
1299
|
}
|
|
1237
1300
|
}, [session.status]);
|
|
1238
|
-
|
|
1301
|
+
useEffect4(() => {
|
|
1239
1302
|
if (session.status === "authenticated") {
|
|
1240
1303
|
api.fetchDrafts().then((drafts) => {
|
|
1241
1304
|
dispatch({ type: "SET_DRAFTS", drafts });
|
|
@@ -1243,7 +1306,7 @@ function FeedbackWidget({
|
|
|
1243
1306
|
});
|
|
1244
1307
|
}
|
|
1245
1308
|
}, [state.panelOpen, session.status, api]);
|
|
1246
|
-
|
|
1309
|
+
useEffect4(() => {
|
|
1247
1310
|
if (!hotkey) return;
|
|
1248
1311
|
const parsed = parseHotkey(hotkey);
|
|
1249
1312
|
function handler(e) {
|
|
@@ -1303,10 +1366,13 @@ function FeedbackWidget({
|
|
|
1303
1366
|
clickX: centerX,
|
|
1304
1367
|
clickY: centerY
|
|
1305
1368
|
};
|
|
1369
|
+
const isDrag = rect.width > 20 && rect.height > 20;
|
|
1306
1370
|
let screenshot = null;
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1371
|
+
if (isDrag) {
|
|
1372
|
+
try {
|
|
1373
|
+
screenshot = await captureScreenshot(rect);
|
|
1374
|
+
} catch {
|
|
1375
|
+
}
|
|
1310
1376
|
}
|
|
1311
1377
|
dispatch({ type: "ELEMENT_PICKED", context, screenshot });
|
|
1312
1378
|
}, []);
|