@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.
@@ -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/api";
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 resolvedBaseUrl = baseUrl || DefaultKeletBaseUrl;
389
- const url = `${resolvedBaseUrl}/projects/${project}/feedback`;
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
- tx_id: data.tx_id,
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
- // Include trigger_name if needed in the future
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
- tx_id: txIdProp,
556
+ session_id: sessionIdProp,
460
557
  extra_metadata,
461
558
  trigger_name
462
559
  }) => {
463
- const tx_id = typeof txIdProp === "function" ? txIdProp() : txIdProp;
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
- }, [tx_id, defaultText]);
577
+ }, [session_id, defaultText]);
481
578
  const handleUpvote = require$$0$1.useCallback(async () => {
482
579
  setVote("upvote");
483
580
  const data = {
484
- tx_id,
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, tx_id, extra_metadata, trigger_name]);
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
- tx_id,
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, tx_id, extra_metadata, trigger_name]);
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
- tx_id,
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
- }, [handler, feedbackText, defaultText, tx_id, extra_metadata, trigger_name]);
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
- tx_id,
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, tx_id, options) {
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 tx_id === "function" ? tx_id(endState) : tx_id;
2161
+ const idString = typeof session_id === "function" ? session_id(endState) : session_id;
2058
2162
  feedbackHandler({
2059
- tx_id: idString,
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, tx_id, diffType, feedbackHandler]
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
- tx_id,
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, tx_id, options) {
2255
+ function useFeedbackState(initialState, session_id, options) {
2152
2256
  const [state, setStateInternal] = require$$0$1.useState(initialState);
2153
- const { notifyChange } = useStateChangeTracking(state, tx_id, options);
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);