@kelet-ai/feedback-ui 1.0.2 → 1.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/README.md +19 -19
- package/dist/components/vote-feedback.d.ts +1 -1
- package/dist/feedback-ui.es.js +128 -24
- package/dist/feedback-ui.es.js.map +1 -1
- package/dist/feedback-ui.es.min.js +732 -657
- package/dist/feedback-ui.es.min.js.map +1 -1
- package/dist/feedback-ui.umd.js +127 -23
- package/dist/feedback-ui.umd.js.map +1 -1
- package/dist/feedback-ui.umd.min.js +12 -12
- package/dist/feedback-ui.umd.min.js.map +1 -1
- package/dist/hooks/feedback-state/use-feedback-reducer.d.ts +5 -5
- package/dist/hooks/feedback-state/use-feedback-state.d.ts +6 -6
- package/dist/hooks/feedback-state/use-state-change-tracking.d.ts +2 -2
- package/dist/lib/event-capture.d.ts +22 -0
- package/dist/types/index.d.ts +22 -2
- package/package.json +1 -1
package/dist/feedback-ui.umd.js
CHANGED
|
@@ -355,8 +355,90 @@
|
|
|
355
355
|
return jsxRuntime.exports;
|
|
356
356
|
}
|
|
357
357
|
var jsxRuntimeExports = requireJsxRuntime();
|
|
358
|
+
let latestEvent = null;
|
|
359
|
+
let isInitialized = false;
|
|
360
|
+
function serializeTarget(target) {
|
|
361
|
+
if (!target || !(target instanceof Element)) return "unknown";
|
|
362
|
+
const el = target;
|
|
363
|
+
if (el.id) return `#${el.id}`;
|
|
364
|
+
const dataId = el.getAttribute("data-feedback-id");
|
|
365
|
+
if (dataId) return `[data-feedback-id="${dataId}"]`;
|
|
366
|
+
const path = [];
|
|
367
|
+
let current = el;
|
|
368
|
+
let depth = 0;
|
|
369
|
+
const MAX_DEPTH = 5;
|
|
370
|
+
while (current && current !== document.body && depth < MAX_DEPTH) {
|
|
371
|
+
let selector = current.tagName.toLowerCase();
|
|
372
|
+
if (current.classList.length > 0) {
|
|
373
|
+
const className = Array.from(current.classList)[0];
|
|
374
|
+
selector += `.${className}`;
|
|
375
|
+
}
|
|
376
|
+
const parent = current.parentElement;
|
|
377
|
+
if (parent) {
|
|
378
|
+
const siblings = Array.from(parent.children);
|
|
379
|
+
const index = siblings.indexOf(current) + 1;
|
|
380
|
+
if (siblings.length > 1) {
|
|
381
|
+
selector += `:nth-child(${index})`;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
path.unshift(selector);
|
|
385
|
+
current = parent;
|
|
386
|
+
depth++;
|
|
387
|
+
}
|
|
388
|
+
return path.join(" > ");
|
|
389
|
+
}
|
|
390
|
+
function getTargetText(target) {
|
|
391
|
+
if (!target || !(target instanceof Element)) return "";
|
|
392
|
+
const el = target;
|
|
393
|
+
const ariaLabel = el.getAttribute("aria-label");
|
|
394
|
+
if (ariaLabel) return ariaLabel.substring(0, 50);
|
|
395
|
+
const text = el.textContent?.trim() || "";
|
|
396
|
+
return text.substring(0, 50);
|
|
397
|
+
}
|
|
398
|
+
function captureEvent(e) {
|
|
399
|
+
const captured = {
|
|
400
|
+
type: e.type,
|
|
401
|
+
targetSelector: serializeTarget(e.target),
|
|
402
|
+
targetText: getTargetText(e.target),
|
|
403
|
+
timestamp: Date.now()
|
|
404
|
+
};
|
|
405
|
+
if (e instanceof MouseEvent) {
|
|
406
|
+
captured.coordinates = { x: e.clientX, y: e.clientY };
|
|
407
|
+
} else if (e instanceof KeyboardEvent) {
|
|
408
|
+
captured.key = e.key;
|
|
409
|
+
}
|
|
410
|
+
return captured;
|
|
411
|
+
}
|
|
412
|
+
function initEventCapture() {
|
|
413
|
+
if (isInitialized || typeof window === "undefined") return;
|
|
414
|
+
const eventTypes = ["click", "keydown", "submit", "change"];
|
|
415
|
+
eventTypes.forEach((type) => {
|
|
416
|
+
window.addEventListener(
|
|
417
|
+
type,
|
|
418
|
+
(e) => {
|
|
419
|
+
latestEvent = captureEvent(e);
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
capture: true,
|
|
423
|
+
// Intercept before React handlers
|
|
424
|
+
passive: true
|
|
425
|
+
// Better scroll performance
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
isInitialized = true;
|
|
430
|
+
}
|
|
431
|
+
function getLatestEvent() {
|
|
432
|
+
if (!latestEvent) return null;
|
|
433
|
+
const age = Date.now() - latestEvent.timestamp;
|
|
434
|
+
if (age > 1e4) {
|
|
435
|
+
latestEvent = null;
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
return { ...latestEvent };
|
|
439
|
+
}
|
|
358
440
|
const KeletContext = require$$0$1.createContext(null);
|
|
359
|
-
const DefaultKeletBaseUrl = "https://api.kelet.ai
|
|
441
|
+
const DefaultKeletBaseUrl = "https://api.kelet.ai";
|
|
360
442
|
const useKelet = () => {
|
|
361
443
|
const context = require$$0$1.useContext(KeletContext);
|
|
362
444
|
if (!context) {
|
|
@@ -377,6 +459,9 @@
|
|
|
377
459
|
}
|
|
378
460
|
};
|
|
379
461
|
const KeletProvider = ({ apiKey, project, baseUrl, children }) => {
|
|
462
|
+
require$$0$1.useEffect(() => {
|
|
463
|
+
initEventCapture();
|
|
464
|
+
}, []);
|
|
380
465
|
const parentContext = require$$0$1.useContext(KeletContext);
|
|
381
466
|
const resolvedApiKey = apiKey || parentContext?.api_key;
|
|
382
467
|
if (!resolvedApiKey) {
|
|
@@ -384,17 +469,29 @@
|
|
|
384
469
|
"apiKey is required either directly or from a parent KeletProvider"
|
|
385
470
|
);
|
|
386
471
|
}
|
|
472
|
+
let resolvedBaseUrl = baseUrl || DefaultKeletBaseUrl;
|
|
473
|
+
if (resolvedBaseUrl.endsWith("/api")) {
|
|
474
|
+
resolvedBaseUrl = resolvedBaseUrl.slice(0, -4);
|
|
475
|
+
}
|
|
476
|
+
if (resolvedBaseUrl.endsWith("/")) {
|
|
477
|
+
resolvedBaseUrl = resolvedBaseUrl.slice(0, -1);
|
|
478
|
+
}
|
|
387
479
|
const feedback = async (data) => {
|
|
388
|
-
const
|
|
389
|
-
const
|
|
480
|
+
const url = `${resolvedBaseUrl}/api/projects/${project}/signal`;
|
|
481
|
+
const capturedEvent = getLatestEvent();
|
|
482
|
+
const metadata = {
|
|
483
|
+
...data.extra_metadata ?? {},
|
|
484
|
+
...capturedEvent && { $dom_event: capturedEvent }
|
|
485
|
+
};
|
|
390
486
|
const req = {
|
|
391
|
-
|
|
487
|
+
session_id: data.session_id,
|
|
392
488
|
source: data.source || "EXPLICIT",
|
|
393
489
|
vote: data.vote,
|
|
394
490
|
explanation: data.explanation,
|
|
395
491
|
correction: data.correction,
|
|
396
|
-
selection: data.selection
|
|
397
|
-
|
|
492
|
+
selection: data.selection,
|
|
493
|
+
trigger_name: data.trigger_name,
|
|
494
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
398
495
|
};
|
|
399
496
|
const response = await fetch(url, {
|
|
400
497
|
method: "POST",
|
|
@@ -456,11 +553,11 @@
|
|
|
456
553
|
children,
|
|
457
554
|
onFeedback,
|
|
458
555
|
defaultText = "",
|
|
459
|
-
|
|
556
|
+
session_id: sessionIdProp,
|
|
460
557
|
extra_metadata,
|
|
461
558
|
trigger_name
|
|
462
559
|
}) => {
|
|
463
|
-
const
|
|
560
|
+
const session_id = typeof sessionIdProp === "function" ? sessionIdProp() : sessionIdProp;
|
|
464
561
|
const [showPopover, setShowPopover] = require$$0$1.useState(false);
|
|
465
562
|
const [feedbackText, setFeedbackText] = require$$0$1.useState(defaultText);
|
|
466
563
|
const [isSubmitting, setIsSubmitting] = require$$0$1.useState(false);
|
|
@@ -477,11 +574,11 @@
|
|
|
477
574
|
setIsSubmitting(false);
|
|
478
575
|
setVote(null);
|
|
479
576
|
setTimeout(() => triggerRef.current?.focus(), 0);
|
|
480
|
-
}, [
|
|
577
|
+
}, [session_id, defaultText]);
|
|
481
578
|
const handleUpvote = require$$0$1.useCallback(async () => {
|
|
482
579
|
setVote("upvote");
|
|
483
580
|
const data = {
|
|
484
|
-
|
|
581
|
+
session_id,
|
|
485
582
|
vote: "upvote",
|
|
486
583
|
...extra_metadata && { extra_metadata },
|
|
487
584
|
...trigger_name && { trigger_name }
|
|
@@ -492,12 +589,12 @@
|
|
|
492
589
|
} finally {
|
|
493
590
|
setIsSubmitting(false);
|
|
494
591
|
}
|
|
495
|
-
}, [handler,
|
|
592
|
+
}, [handler, session_id, extra_metadata, trigger_name]);
|
|
496
593
|
const handleDownvote = require$$0$1.useCallback(async () => {
|
|
497
594
|
setVote("downvote");
|
|
498
595
|
if (handler) {
|
|
499
596
|
const data = {
|
|
500
|
-
|
|
597
|
+
session_id,
|
|
501
598
|
vote: "downvote",
|
|
502
599
|
...extra_metadata && { extra_metadata },
|
|
503
600
|
...trigger_name && { trigger_name }
|
|
@@ -520,7 +617,7 @@
|
|
|
520
617
|
document.body.appendChild(announcement);
|
|
521
618
|
setTimeout(() => document.body.removeChild(announcement), 1e3);
|
|
522
619
|
}, 0);
|
|
523
|
-
}, [handler,
|
|
620
|
+
}, [handler, session_id, extra_metadata, trigger_name]);
|
|
524
621
|
const handleTextareaChange = require$$0$1.useCallback(
|
|
525
622
|
(e) => {
|
|
526
623
|
setFeedbackText(e.target.value);
|
|
@@ -531,7 +628,7 @@
|
|
|
531
628
|
const hasText = feedbackText.trim().length > 0;
|
|
532
629
|
if (hasText) {
|
|
533
630
|
const data = {
|
|
534
|
-
|
|
631
|
+
session_id,
|
|
535
632
|
vote: "downvote",
|
|
536
633
|
explanation: feedbackText,
|
|
537
634
|
...extra_metadata && { extra_metadata },
|
|
@@ -555,7 +652,14 @@
|
|
|
555
652
|
document.body.appendChild(announcement);
|
|
556
653
|
setTimeout(() => document.body.removeChild(announcement), 1e3);
|
|
557
654
|
}
|
|
558
|
-
}, [
|
|
655
|
+
}, [
|
|
656
|
+
handler,
|
|
657
|
+
feedbackText,
|
|
658
|
+
defaultText,
|
|
659
|
+
session_id,
|
|
660
|
+
extra_metadata,
|
|
661
|
+
trigger_name
|
|
662
|
+
]);
|
|
559
663
|
const handleKeyDown = require$$0$1.useCallback(
|
|
560
664
|
(e) => {
|
|
561
665
|
if (e.key === "Escape") {
|
|
@@ -604,7 +708,7 @@
|
|
|
604
708
|
triggerRef,
|
|
605
709
|
popoverId,
|
|
606
710
|
triggerId,
|
|
607
|
-
|
|
711
|
+
session_id,
|
|
608
712
|
extra_metadata,
|
|
609
713
|
trigger_name
|
|
610
714
|
};
|
|
@@ -2019,7 +2123,7 @@
|
|
|
2019
2123
|
return String(value);
|
|
2020
2124
|
}
|
|
2021
2125
|
}
|
|
2022
|
-
function useStateChangeTracking(currentState,
|
|
2126
|
+
function useStateChangeTracking(currentState, session_id, options) {
|
|
2023
2127
|
const defaultFeedbackHandler = useDefaultFeedbackHandler();
|
|
2024
2128
|
const feedbackHandler = options?.onFeedback || defaultFeedbackHandler;
|
|
2025
2129
|
const debounceMs = options?.debounceMs ?? 3e3;
|
|
@@ -2054,9 +2158,9 @@
|
|
|
2054
2158
|
} else {
|
|
2055
2159
|
vote = diffPercentage > 0.5 ? "downvote" : "upvote";
|
|
2056
2160
|
}
|
|
2057
|
-
const idString = typeof
|
|
2161
|
+
const idString = typeof session_id === "function" ? session_id(endState) : session_id;
|
|
2058
2162
|
feedbackHandler({
|
|
2059
|
-
|
|
2163
|
+
session_id: idString,
|
|
2060
2164
|
vote,
|
|
2061
2165
|
explanation: `State change with diff percentage: ${(diffPercentage * 100).toFixed(1)}%`,
|
|
2062
2166
|
correction: diffString,
|
|
@@ -2065,7 +2169,7 @@
|
|
|
2065
2169
|
trigger_name: triggerName
|
|
2066
2170
|
});
|
|
2067
2171
|
},
|
|
2068
|
-
[options,
|
|
2172
|
+
[options, session_id, diffType, feedbackHandler]
|
|
2069
2173
|
);
|
|
2070
2174
|
const notifyChange = require$$0$1.useCallback(
|
|
2071
2175
|
(trigger_name) => {
|
|
@@ -2134,7 +2238,7 @@
|
|
|
2134
2238
|
};
|
|
2135
2239
|
}, [
|
|
2136
2240
|
currentState,
|
|
2137
|
-
|
|
2241
|
+
session_id,
|
|
2138
2242
|
options,
|
|
2139
2243
|
feedbackHandler,
|
|
2140
2244
|
diffType,
|
|
@@ -2148,9 +2252,9 @@
|
|
|
2148
2252
|
notifyChange
|
|
2149
2253
|
};
|
|
2150
2254
|
}
|
|
2151
|
-
function useFeedbackState(initialState,
|
|
2255
|
+
function useFeedbackState(initialState, session_id, options) {
|
|
2152
2256
|
const [state, setStateInternal] = require$$0$1.useState(initialState);
|
|
2153
|
-
const { notifyChange } = useStateChangeTracking(state,
|
|
2257
|
+
const { notifyChange } = useStateChangeTracking(state, session_id, options);
|
|
2154
2258
|
const setState = require$$0$1.useCallback(
|
|
2155
2259
|
(value, trigger_name) => {
|
|
2156
2260
|
notifyChange(trigger_name);
|