@kelet-ai/feedback-ui 1.0.2 → 1.1.1

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.
@@ -355,6 +355,88 @@
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
441
  const DefaultKeletBaseUrl = "https://api.kelet.ai/api";
360
442
  const useKelet = () => {
@@ -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) {
@@ -386,15 +471,21 @@
386
471
  }
387
472
  const feedback = async (data) => {
388
473
  const resolvedBaseUrl = baseUrl || DefaultKeletBaseUrl;
389
- const url = `${resolvedBaseUrl}/projects/${project}/feedback`;
474
+ const url = `${resolvedBaseUrl}/projects/${project}/signal`;
475
+ const capturedEvent = getLatestEvent();
476
+ const metadata = {
477
+ ...data.extra_metadata ?? {},
478
+ ...capturedEvent && { $dom_event: capturedEvent }
479
+ };
390
480
  const req = {
391
- tx_id: data.tx_id,
481
+ session_id: data.session_id,
392
482
  source: data.source || "EXPLICIT",
393
483
  vote: data.vote,
394
484
  explanation: data.explanation,
395
485
  correction: data.correction,
396
- selection: data.selection
397
- // Include trigger_name if needed in the future
486
+ selection: data.selection,
487
+ trigger_name: data.trigger_name,
488
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0
398
489
  };
399
490
  const response = await fetch(url, {
400
491
  method: "POST",
@@ -456,11 +547,11 @@
456
547
  children,
457
548
  onFeedback,
458
549
  defaultText = "",
459
- tx_id: txIdProp,
550
+ session_id: sessionIdProp,
460
551
  extra_metadata,
461
552
  trigger_name
462
553
  }) => {
463
- const tx_id = typeof txIdProp === "function" ? txIdProp() : txIdProp;
554
+ const session_id = typeof sessionIdProp === "function" ? sessionIdProp() : sessionIdProp;
464
555
  const [showPopover, setShowPopover] = require$$0$1.useState(false);
465
556
  const [feedbackText, setFeedbackText] = require$$0$1.useState(defaultText);
466
557
  const [isSubmitting, setIsSubmitting] = require$$0$1.useState(false);
@@ -477,11 +568,11 @@
477
568
  setIsSubmitting(false);
478
569
  setVote(null);
479
570
  setTimeout(() => triggerRef.current?.focus(), 0);
480
- }, [tx_id, defaultText]);
571
+ }, [session_id, defaultText]);
481
572
  const handleUpvote = require$$0$1.useCallback(async () => {
482
573
  setVote("upvote");
483
574
  const data = {
484
- tx_id,
575
+ session_id,
485
576
  vote: "upvote",
486
577
  ...extra_metadata && { extra_metadata },
487
578
  ...trigger_name && { trigger_name }
@@ -492,12 +583,12 @@
492
583
  } finally {
493
584
  setIsSubmitting(false);
494
585
  }
495
- }, [handler, tx_id, extra_metadata, trigger_name]);
586
+ }, [handler, session_id, extra_metadata, trigger_name]);
496
587
  const handleDownvote = require$$0$1.useCallback(async () => {
497
588
  setVote("downvote");
498
589
  if (handler) {
499
590
  const data = {
500
- tx_id,
591
+ session_id,
501
592
  vote: "downvote",
502
593
  ...extra_metadata && { extra_metadata },
503
594
  ...trigger_name && { trigger_name }
@@ -520,7 +611,7 @@
520
611
  document.body.appendChild(announcement);
521
612
  setTimeout(() => document.body.removeChild(announcement), 1e3);
522
613
  }, 0);
523
- }, [handler, tx_id, extra_metadata, trigger_name]);
614
+ }, [handler, session_id, extra_metadata, trigger_name]);
524
615
  const handleTextareaChange = require$$0$1.useCallback(
525
616
  (e) => {
526
617
  setFeedbackText(e.target.value);
@@ -531,7 +622,7 @@
531
622
  const hasText = feedbackText.trim().length > 0;
532
623
  if (hasText) {
533
624
  const data = {
534
- tx_id,
625
+ session_id,
535
626
  vote: "downvote",
536
627
  explanation: feedbackText,
537
628
  ...extra_metadata && { extra_metadata },
@@ -555,7 +646,14 @@
555
646
  document.body.appendChild(announcement);
556
647
  setTimeout(() => document.body.removeChild(announcement), 1e3);
557
648
  }
558
- }, [handler, feedbackText, defaultText, tx_id, extra_metadata, trigger_name]);
649
+ }, [
650
+ handler,
651
+ feedbackText,
652
+ defaultText,
653
+ session_id,
654
+ extra_metadata,
655
+ trigger_name
656
+ ]);
559
657
  const handleKeyDown = require$$0$1.useCallback(
560
658
  (e) => {
561
659
  if (e.key === "Escape") {
@@ -604,7 +702,7 @@
604
702
  triggerRef,
605
703
  popoverId,
606
704
  triggerId,
607
- tx_id,
705
+ session_id,
608
706
  extra_metadata,
609
707
  trigger_name
610
708
  };
@@ -2019,7 +2117,7 @@
2019
2117
  return String(value);
2020
2118
  }
2021
2119
  }
2022
- function useStateChangeTracking(currentState, tx_id, options) {
2120
+ function useStateChangeTracking(currentState, session_id, options) {
2023
2121
  const defaultFeedbackHandler = useDefaultFeedbackHandler();
2024
2122
  const feedbackHandler = options?.onFeedback || defaultFeedbackHandler;
2025
2123
  const debounceMs = options?.debounceMs ?? 3e3;
@@ -2054,9 +2152,9 @@
2054
2152
  } else {
2055
2153
  vote = diffPercentage > 0.5 ? "downvote" : "upvote";
2056
2154
  }
2057
- const idString = typeof tx_id === "function" ? tx_id(endState) : tx_id;
2155
+ const idString = typeof session_id === "function" ? session_id(endState) : session_id;
2058
2156
  feedbackHandler({
2059
- tx_id: idString,
2157
+ session_id: idString,
2060
2158
  vote,
2061
2159
  explanation: `State change with diff percentage: ${(diffPercentage * 100).toFixed(1)}%`,
2062
2160
  correction: diffString,
@@ -2065,7 +2163,7 @@
2065
2163
  trigger_name: triggerName
2066
2164
  });
2067
2165
  },
2068
- [options, tx_id, diffType, feedbackHandler]
2166
+ [options, session_id, diffType, feedbackHandler]
2069
2167
  );
2070
2168
  const notifyChange = require$$0$1.useCallback(
2071
2169
  (trigger_name) => {
@@ -2134,7 +2232,7 @@
2134
2232
  };
2135
2233
  }, [
2136
2234
  currentState,
2137
- tx_id,
2235
+ session_id,
2138
2236
  options,
2139
2237
  feedbackHandler,
2140
2238
  diffType,
@@ -2148,9 +2246,9 @@
2148
2246
  notifyChange
2149
2247
  };
2150
2248
  }
2151
- function useFeedbackState(initialState, tx_id, options) {
2249
+ function useFeedbackState(initialState, session_id, options) {
2152
2250
  const [state, setStateInternal] = require$$0$1.useState(initialState);
2153
- const { notifyChange } = useStateChangeTracking(state, tx_id, options);
2251
+ const { notifyChange } = useStateChangeTracking(state, session_id, options);
2154
2252
  const setState = require$$0$1.useCallback(
2155
2253
  (value, trigger_name) => {
2156
2254
  notifyChange(trigger_name);