@kelet-ai/feedback-ui 1.0.1 → 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.
- package/README.md +19 -36
- package/dist/components/vote-feedback.d.ts +1 -1
- package/dist/feedback-ui.es.js +120 -54
- package/dist/feedback-ui.es.js.map +1 -1
- package/dist/feedback-ui.es.min.js +519 -473
- package/dist/feedback-ui.es.min.js.map +1 -1
- package/dist/feedback-ui.umd.js +119 -53
- package/dist/feedback-ui.umd.js.map +1 -1
- package/dist/feedback-ui.umd.min.js +11 -11
- 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/index.d.ts +0 -1
- package/dist/lib/event-capture.d.ts +22 -0
- package/dist/types/index.d.ts +22 -2
- package/package.json +3 -13
- package/dist/lib/otel-trace.d.ts +0 -21
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ import { ShadcnVoteFeedback } from '@/components/ui/vote-feedback';
|
|
|
28
28
|
function App() {
|
|
29
29
|
return (
|
|
30
30
|
<ShadcnVoteFeedback
|
|
31
|
-
|
|
31
|
+
session_id="my-feature"
|
|
32
32
|
onFeedback={feedback => console.log(feedback)}
|
|
33
33
|
variant="outline"
|
|
34
34
|
/>
|
|
@@ -91,7 +91,7 @@ Users explicitly vote and provide comments:
|
|
|
91
91
|
```tsx
|
|
92
92
|
import { VoteFeedback } from '@kelet-ai/feedback-ui';
|
|
93
93
|
|
|
94
|
-
<VoteFeedback.Root onFeedback={handleFeedback}
|
|
94
|
+
<VoteFeedback.Root onFeedback={handleFeedback} session_id="ai-response">
|
|
95
95
|
<VoteFeedback.UpvoteButton>👍 Helpful</VoteFeedback.UpvoteButton>
|
|
96
96
|
<VoteFeedback.DownvoteButton>👎 Not helpful</VoteFeedback.DownvoteButton>
|
|
97
97
|
<VoteFeedback.Popover>
|
|
@@ -158,14 +158,14 @@ Understanding these fundamental concepts will help you implement feedback collec
|
|
|
158
158
|
**Best Practice**: Use traceable IDs from your logging system (session ID, trace ID, request ID)
|
|
159
159
|
|
|
160
160
|
```tsx
|
|
161
|
-
// ✅ Good: Traceable
|
|
162
|
-
<VoteFeedback.Root
|
|
161
|
+
// ✅ Good: Traceable session_id
|
|
162
|
+
<VoteFeedback.Root session_id="session-abc123-ai-response-456"/>
|
|
163
163
|
|
|
164
|
-
// ✅ Good: Content-based
|
|
165
|
-
<VoteFeedback.Root
|
|
164
|
+
// ✅ Good: Content-based session_id
|
|
165
|
+
<VoteFeedback.Root session_id={`article-${articleId}-section-${sectionId}`}/>
|
|
166
166
|
|
|
167
|
-
// ❌ Poor: Generic
|
|
168
|
-
<VoteFeedback.Root
|
|
167
|
+
// ❌ Poor: Generic session_id
|
|
168
|
+
<VoteFeedback.Root session_id="feedback"/>
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
### **📊 Feedback Sources**
|
|
@@ -296,8 +296,8 @@ const [data, setData] = useFeedbackState(initial, 'tracker', {
|
|
|
296
296
|
#### **Identifiers**
|
|
297
297
|
|
|
298
298
|
✅ Use traceable session/request IDs
|
|
299
|
-
✅ Include context in
|
|
300
|
-
✅ Keep
|
|
299
|
+
✅ Include context in session_id structure
|
|
300
|
+
✅ Keep session_ids consistent across related actions
|
|
301
301
|
|
|
302
302
|
#### **Feedback Sources**
|
|
303
303
|
|
|
@@ -319,23 +319,6 @@ const [data, setData] = useFeedbackState(initial, 'tracker', {
|
|
|
319
319
|
|
|
320
320
|
---
|
|
321
321
|
|
|
322
|
-
## 🔗 OpenTelemetry Integration
|
|
323
|
-
|
|
324
|
-
Prefer W3C traceparent to correlate feedback with distributed traces:
|
|
325
|
-
|
|
326
|
-
```tsx
|
|
327
|
-
import { VoteFeedback, getTraceParent } from '@kelet-ai/feedback-ui';
|
|
328
|
-
|
|
329
|
-
<VoteFeedback.Root tx_id={getTraceParent} onFeedback={handleFeedback}>
|
|
330
|
-
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
331
|
-
<VoteFeedback.DownvoteButton>👎</VoteFeedback.DownvoteButton>
|
|
332
|
-
</VoteFeedback.Root>;
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
Requires `@opentelemetry/api` for active context. If missing at runtime, an error is thrown.
|
|
336
|
-
|
|
337
|
-
---
|
|
338
|
-
|
|
339
322
|
## 🔧 Core Components
|
|
340
323
|
|
|
341
324
|
### **VoteFeedback.Root**
|
|
@@ -344,7 +327,7 @@ Main container component that manages feedback state.
|
|
|
344
327
|
|
|
345
328
|
```tsx
|
|
346
329
|
<VoteFeedback.Root
|
|
347
|
-
|
|
330
|
+
session_id="unique-id" // Required: Unique tracking ID
|
|
348
331
|
onFeedback={handleFeedback} // Required: Callback function
|
|
349
332
|
trigger_name="user_feedback" // Optional: Categorization
|
|
350
333
|
extra_metadata={{ page: 'home' }} // Optional: Additional data
|
|
@@ -393,7 +376,7 @@ const [count, setCount] = useFeedbackState(0, 'counter-widget');
|
|
|
393
376
|
```tsx
|
|
394
377
|
const [profile, setProfile] = useFeedbackState(
|
|
395
378
|
{ name: '', email: '' },
|
|
396
|
-
state => `profile-${state.email}`, // Dynamic
|
|
379
|
+
state => `profile-${state.email}`, // Dynamic session_id
|
|
397
380
|
{
|
|
398
381
|
debounceMs: 2000, // Wait time before sending feedback
|
|
399
382
|
diffType: 'object', // Format: 'git' | 'object' | 'json'
|
|
@@ -483,7 +466,7 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
483
466
|
|
|
484
467
|
```tsx
|
|
485
468
|
<VoteFeedback.Root
|
|
486
|
-
|
|
469
|
+
session_id="ai-response-123"
|
|
487
470
|
onFeedback={handleFeedback}
|
|
488
471
|
trigger_name="ai_evaluation"
|
|
489
472
|
extra_metadata={{
|
|
@@ -509,7 +492,7 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
509
492
|
|
|
510
493
|
```typescript
|
|
511
494
|
interface FeedbackData {
|
|
512
|
-
|
|
495
|
+
session_id: string; // Unique tracking ID
|
|
513
496
|
vote: 'upvote' | 'downvote'; // User's vote
|
|
514
497
|
explanation?: string; // Optional user comment
|
|
515
498
|
extra_metadata?: Record<string, any>; // Additional context data
|
|
@@ -524,7 +507,7 @@ interface FeedbackData {
|
|
|
524
507
|
|
|
525
508
|
| Prop | Type | Required | Description |
|
|
526
509
|
| ---------------- | ------------------------------ | -------- | ----------------------------------- |
|
|
527
|
-
| `
|
|
510
|
+
| `session_id` | `string` | ✅ | Unique session ID for tracking |
|
|
528
511
|
| `onFeedback` | `(data: FeedbackData) => void` | ✅ | Callback when feedback is submitted |
|
|
529
512
|
| `trigger_name` | `string` | ❌ | Optional categorization tag |
|
|
530
513
|
| `extra_metadata` | `object` | ❌ | Additional context data |
|
|
@@ -610,9 +593,9 @@ bun run checks # Run all quality checks (lint, format, typecheck, tests)
|
|
|
610
593
|
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
611
594
|
</VoteFeedback.Root>
|
|
612
595
|
|
|
613
|
-
// ✅ Include required
|
|
596
|
+
// ✅ Include required session_id and onFeedback
|
|
614
597
|
<VoteFeedback.Root
|
|
615
|
-
|
|
598
|
+
session_id="my-feature"
|
|
616
599
|
onFeedback={handleFeedback}
|
|
617
600
|
>
|
|
618
601
|
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
@@ -648,14 +631,14 @@ const [value, setValue] = useFeedbackState('initial', 'test', {
|
|
|
648
631
|
|
|
649
632
|
### **Best Practices**
|
|
650
633
|
|
|
651
|
-
✅ **Use unique
|
|
634
|
+
✅ **Use unique session_ids** for each feedback instance - the session_id should be traceable back to the session's log
|
|
652
635
|
and allow us to understand the context of the feedback.
|
|
653
636
|
✅ **Handle feedback data asynchronously** in your callback
|
|
654
637
|
✅ **Test keyboard navigation** in your implementation
|
|
655
638
|
✅ **Provide meaningful trigger names** for categorization
|
|
656
639
|
✅ **Include relevant metadata** for context
|
|
657
640
|
|
|
658
|
-
❌ **Don't use the same
|
|
641
|
+
❌ **Don't use the same session_id** for multiple components. A session_id should be traced back to the session's log -
|
|
659
642
|
allows us to understand the context of the feedback.
|
|
660
643
|
|
|
661
644
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DownvoteButtonProps, PopoverProps, SubmitButtonProps, TextareaProps, UpvoteButtonProps, VoteFeedbackRootProps } from '../types';
|
|
2
2
|
export declare const VoteFeedback: {
|
|
3
|
-
Root: ({ children, onFeedback, defaultText,
|
|
3
|
+
Root: ({ children, onFeedback, defaultText, session_id: sessionIdProp, extra_metadata, trigger_name, }: VoteFeedbackRootProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
UpvoteButton: ({ asChild, children, onClick, ...props }: UpvoteButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
DownvoteButton: ({ asChild, children, onClick, ...props }: DownvoteButtonProps) => string | number | bigint | true | Iterable<import('react').ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<import('react').ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
Popover: ({ asChild, children, ...props }: PopoverProps) => import("react/jsx-runtime").JSX.Element | null;
|
package/dist/feedback-ui.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import require$$0$1, { createContext, useContext, useCallback, isValidElement, cloneElement, useState, useRef, useId
|
|
1
|
+
import require$$0$1, { createContext, useEffect, useContext, useCallback, isValidElement, cloneElement, useState, useRef, useId } from "react";
|
|
2
2
|
function getDefaultExportFromCjs(x) {
|
|
3
3
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
4
4
|
}
|
|
@@ -352,6 +352,88 @@ function requireJsxRuntime() {
|
|
|
352
352
|
return jsxRuntime.exports;
|
|
353
353
|
}
|
|
354
354
|
var jsxRuntimeExports = requireJsxRuntime();
|
|
355
|
+
let latestEvent = null;
|
|
356
|
+
let isInitialized = false;
|
|
357
|
+
function serializeTarget(target) {
|
|
358
|
+
if (!target || !(target instanceof Element)) return "unknown";
|
|
359
|
+
const el = target;
|
|
360
|
+
if (el.id) return `#${el.id}`;
|
|
361
|
+
const dataId = el.getAttribute("data-feedback-id");
|
|
362
|
+
if (dataId) return `[data-feedback-id="${dataId}"]`;
|
|
363
|
+
const path = [];
|
|
364
|
+
let current = el;
|
|
365
|
+
let depth = 0;
|
|
366
|
+
const MAX_DEPTH = 5;
|
|
367
|
+
while (current && current !== document.body && depth < MAX_DEPTH) {
|
|
368
|
+
let selector = current.tagName.toLowerCase();
|
|
369
|
+
if (current.classList.length > 0) {
|
|
370
|
+
const className = Array.from(current.classList)[0];
|
|
371
|
+
selector += `.${className}`;
|
|
372
|
+
}
|
|
373
|
+
const parent = current.parentElement;
|
|
374
|
+
if (parent) {
|
|
375
|
+
const siblings = Array.from(parent.children);
|
|
376
|
+
const index = siblings.indexOf(current) + 1;
|
|
377
|
+
if (siblings.length > 1) {
|
|
378
|
+
selector += `:nth-child(${index})`;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
path.unshift(selector);
|
|
382
|
+
current = parent;
|
|
383
|
+
depth++;
|
|
384
|
+
}
|
|
385
|
+
return path.join(" > ");
|
|
386
|
+
}
|
|
387
|
+
function getTargetText(target) {
|
|
388
|
+
if (!target || !(target instanceof Element)) return "";
|
|
389
|
+
const el = target;
|
|
390
|
+
const ariaLabel = el.getAttribute("aria-label");
|
|
391
|
+
if (ariaLabel) return ariaLabel.substring(0, 50);
|
|
392
|
+
const text = el.textContent?.trim() || "";
|
|
393
|
+
return text.substring(0, 50);
|
|
394
|
+
}
|
|
395
|
+
function captureEvent(e) {
|
|
396
|
+
const captured = {
|
|
397
|
+
type: e.type,
|
|
398
|
+
targetSelector: serializeTarget(e.target),
|
|
399
|
+
targetText: getTargetText(e.target),
|
|
400
|
+
timestamp: Date.now()
|
|
401
|
+
};
|
|
402
|
+
if (e instanceof MouseEvent) {
|
|
403
|
+
captured.coordinates = { x: e.clientX, y: e.clientY };
|
|
404
|
+
} else if (e instanceof KeyboardEvent) {
|
|
405
|
+
captured.key = e.key;
|
|
406
|
+
}
|
|
407
|
+
return captured;
|
|
408
|
+
}
|
|
409
|
+
function initEventCapture() {
|
|
410
|
+
if (isInitialized || typeof window === "undefined") return;
|
|
411
|
+
const eventTypes = ["click", "keydown", "submit", "change"];
|
|
412
|
+
eventTypes.forEach((type) => {
|
|
413
|
+
window.addEventListener(
|
|
414
|
+
type,
|
|
415
|
+
(e) => {
|
|
416
|
+
latestEvent = captureEvent(e);
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
capture: true,
|
|
420
|
+
// Intercept before React handlers
|
|
421
|
+
passive: true
|
|
422
|
+
// Better scroll performance
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
isInitialized = true;
|
|
427
|
+
}
|
|
428
|
+
function getLatestEvent() {
|
|
429
|
+
if (!latestEvent) return null;
|
|
430
|
+
const age = Date.now() - latestEvent.timestamp;
|
|
431
|
+
if (age > 1e4) {
|
|
432
|
+
latestEvent = null;
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
return { ...latestEvent };
|
|
436
|
+
}
|
|
355
437
|
const KeletContext = createContext(null);
|
|
356
438
|
const DefaultKeletBaseUrl = "https://api.kelet.ai/api";
|
|
357
439
|
const useKelet = () => {
|
|
@@ -374,6 +456,9 @@ const useDefaultFeedbackHandler = () => {
|
|
|
374
456
|
}
|
|
375
457
|
};
|
|
376
458
|
const KeletProvider = ({ apiKey, project, baseUrl, children }) => {
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
initEventCapture();
|
|
461
|
+
}, []);
|
|
377
462
|
const parentContext = useContext(KeletContext);
|
|
378
463
|
const resolvedApiKey = apiKey || parentContext?.api_key;
|
|
379
464
|
if (!resolvedApiKey) {
|
|
@@ -383,15 +468,21 @@ const KeletProvider = ({ apiKey, project, baseUrl, children }) => {
|
|
|
383
468
|
}
|
|
384
469
|
const feedback = async (data) => {
|
|
385
470
|
const resolvedBaseUrl = baseUrl || DefaultKeletBaseUrl;
|
|
386
|
-
const url = `${resolvedBaseUrl}/projects/${project}/
|
|
471
|
+
const url = `${resolvedBaseUrl}/projects/${project}/signal`;
|
|
472
|
+
const capturedEvent = getLatestEvent();
|
|
473
|
+
const metadata = {
|
|
474
|
+
...data.extra_metadata ?? {},
|
|
475
|
+
...capturedEvent && { $dom_event: capturedEvent }
|
|
476
|
+
};
|
|
387
477
|
const req = {
|
|
388
|
-
|
|
478
|
+
session_id: data.session_id,
|
|
389
479
|
source: data.source || "EXPLICIT",
|
|
390
480
|
vote: data.vote,
|
|
391
481
|
explanation: data.explanation,
|
|
392
482
|
correction: data.correction,
|
|
393
|
-
selection: data.selection
|
|
394
|
-
|
|
483
|
+
selection: data.selection,
|
|
484
|
+
trigger_name: data.trigger_name,
|
|
485
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
395
486
|
};
|
|
396
487
|
const response = await fetch(url, {
|
|
397
488
|
method: "POST",
|
|
@@ -453,11 +544,11 @@ const VoteFeedbackRoot = ({
|
|
|
453
544
|
children,
|
|
454
545
|
onFeedback,
|
|
455
546
|
defaultText = "",
|
|
456
|
-
|
|
547
|
+
session_id: sessionIdProp,
|
|
457
548
|
extra_metadata,
|
|
458
549
|
trigger_name
|
|
459
550
|
}) => {
|
|
460
|
-
const
|
|
551
|
+
const session_id = typeof sessionIdProp === "function" ? sessionIdProp() : sessionIdProp;
|
|
461
552
|
const [showPopover, setShowPopover] = useState(false);
|
|
462
553
|
const [feedbackText, setFeedbackText] = useState(defaultText);
|
|
463
554
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -474,11 +565,11 @@ const VoteFeedbackRoot = ({
|
|
|
474
565
|
setIsSubmitting(false);
|
|
475
566
|
setVote(null);
|
|
476
567
|
setTimeout(() => triggerRef.current?.focus(), 0);
|
|
477
|
-
}, [
|
|
568
|
+
}, [session_id, defaultText]);
|
|
478
569
|
const handleUpvote = useCallback(async () => {
|
|
479
570
|
setVote("upvote");
|
|
480
571
|
const data = {
|
|
481
|
-
|
|
572
|
+
session_id,
|
|
482
573
|
vote: "upvote",
|
|
483
574
|
...extra_metadata && { extra_metadata },
|
|
484
575
|
...trigger_name && { trigger_name }
|
|
@@ -489,12 +580,12 @@ const VoteFeedbackRoot = ({
|
|
|
489
580
|
} finally {
|
|
490
581
|
setIsSubmitting(false);
|
|
491
582
|
}
|
|
492
|
-
}, [handler,
|
|
583
|
+
}, [handler, session_id, extra_metadata, trigger_name]);
|
|
493
584
|
const handleDownvote = useCallback(async () => {
|
|
494
585
|
setVote("downvote");
|
|
495
586
|
if (handler) {
|
|
496
587
|
const data = {
|
|
497
|
-
|
|
588
|
+
session_id,
|
|
498
589
|
vote: "downvote",
|
|
499
590
|
...extra_metadata && { extra_metadata },
|
|
500
591
|
...trigger_name && { trigger_name }
|
|
@@ -517,7 +608,7 @@ const VoteFeedbackRoot = ({
|
|
|
517
608
|
document.body.appendChild(announcement);
|
|
518
609
|
setTimeout(() => document.body.removeChild(announcement), 1e3);
|
|
519
610
|
}, 0);
|
|
520
|
-
}, [handler,
|
|
611
|
+
}, [handler, session_id, extra_metadata, trigger_name]);
|
|
521
612
|
const handleTextareaChange = useCallback(
|
|
522
613
|
(e) => {
|
|
523
614
|
setFeedbackText(e.target.value);
|
|
@@ -528,7 +619,7 @@ const VoteFeedbackRoot = ({
|
|
|
528
619
|
const hasText = feedbackText.trim().length > 0;
|
|
529
620
|
if (hasText) {
|
|
530
621
|
const data = {
|
|
531
|
-
|
|
622
|
+
session_id,
|
|
532
623
|
vote: "downvote",
|
|
533
624
|
explanation: feedbackText,
|
|
534
625
|
...extra_metadata && { extra_metadata },
|
|
@@ -552,7 +643,14 @@ const VoteFeedbackRoot = ({
|
|
|
552
643
|
document.body.appendChild(announcement);
|
|
553
644
|
setTimeout(() => document.body.removeChild(announcement), 1e3);
|
|
554
645
|
}
|
|
555
|
-
}, [
|
|
646
|
+
}, [
|
|
647
|
+
handler,
|
|
648
|
+
feedbackText,
|
|
649
|
+
defaultText,
|
|
650
|
+
session_id,
|
|
651
|
+
extra_metadata,
|
|
652
|
+
trigger_name
|
|
653
|
+
]);
|
|
556
654
|
const handleKeyDown = useCallback(
|
|
557
655
|
(e) => {
|
|
558
656
|
if (e.key === "Escape") {
|
|
@@ -601,7 +699,7 @@ const VoteFeedbackRoot = ({
|
|
|
601
699
|
triggerRef,
|
|
602
700
|
popoverId,
|
|
603
701
|
triggerId,
|
|
604
|
-
|
|
702
|
+
session_id,
|
|
605
703
|
extra_metadata,
|
|
606
704
|
trigger_name
|
|
607
705
|
};
|
|
@@ -2016,7 +2114,7 @@ function stringify(value) {
|
|
|
2016
2114
|
return String(value);
|
|
2017
2115
|
}
|
|
2018
2116
|
}
|
|
2019
|
-
function useStateChangeTracking(currentState,
|
|
2117
|
+
function useStateChangeTracking(currentState, session_id, options) {
|
|
2020
2118
|
const defaultFeedbackHandler = useDefaultFeedbackHandler();
|
|
2021
2119
|
const feedbackHandler = options?.onFeedback || defaultFeedbackHandler;
|
|
2022
2120
|
const debounceMs = options?.debounceMs ?? 3e3;
|
|
@@ -2051,9 +2149,9 @@ function useStateChangeTracking(currentState, tx_id, options) {
|
|
|
2051
2149
|
} else {
|
|
2052
2150
|
vote = diffPercentage > 0.5 ? "downvote" : "upvote";
|
|
2053
2151
|
}
|
|
2054
|
-
const idString = typeof
|
|
2152
|
+
const idString = typeof session_id === "function" ? session_id(endState) : session_id;
|
|
2055
2153
|
feedbackHandler({
|
|
2056
|
-
|
|
2154
|
+
session_id: idString,
|
|
2057
2155
|
vote,
|
|
2058
2156
|
explanation: `State change with diff percentage: ${(diffPercentage * 100).toFixed(1)}%`,
|
|
2059
2157
|
correction: diffString,
|
|
@@ -2062,7 +2160,7 @@ function useStateChangeTracking(currentState, tx_id, options) {
|
|
|
2062
2160
|
trigger_name: triggerName
|
|
2063
2161
|
});
|
|
2064
2162
|
},
|
|
2065
|
-
[options,
|
|
2163
|
+
[options, session_id, diffType, feedbackHandler]
|
|
2066
2164
|
);
|
|
2067
2165
|
const notifyChange = useCallback(
|
|
2068
2166
|
(trigger_name) => {
|
|
@@ -2131,7 +2229,7 @@ function useStateChangeTracking(currentState, tx_id, options) {
|
|
|
2131
2229
|
};
|
|
2132
2230
|
}, [
|
|
2133
2231
|
currentState,
|
|
2134
|
-
|
|
2232
|
+
session_id,
|
|
2135
2233
|
options,
|
|
2136
2234
|
feedbackHandler,
|
|
2137
2235
|
diffType,
|
|
@@ -2145,9 +2243,9 @@ function useStateChangeTracking(currentState, tx_id, options) {
|
|
|
2145
2243
|
notifyChange
|
|
2146
2244
|
};
|
|
2147
2245
|
}
|
|
2148
|
-
function useFeedbackState(initialState,
|
|
2246
|
+
function useFeedbackState(initialState, session_id, options) {
|
|
2149
2247
|
const [state, setStateInternal] = useState(initialState);
|
|
2150
|
-
const { notifyChange } = useStateChangeTracking(state,
|
|
2248
|
+
const { notifyChange } = useStateChangeTracking(state, session_id, options);
|
|
2151
2249
|
const setState = useCallback(
|
|
2152
2250
|
(value, trigger_name) => {
|
|
2153
2251
|
notifyChange(trigger_name);
|
|
@@ -2160,42 +2258,10 @@ function useFeedbackState(initialState, tx_id, options) {
|
|
|
2160
2258
|
);
|
|
2161
2259
|
return [state, setState];
|
|
2162
2260
|
}
|
|
2163
|
-
const _loadOtelApi = () => {
|
|
2164
|
-
const g = globalThis;
|
|
2165
|
-
const store = g[Symbol.for("opentelemetry.js.api.1")];
|
|
2166
|
-
const api = store?.api ?? store;
|
|
2167
|
-
if (!api?.context || !api?.propagation) {
|
|
2168
|
-
throw new Error(
|
|
2169
|
-
"@opentelemetry/api not found. Install it and configure a tracer provider."
|
|
2170
|
-
);
|
|
2171
|
-
}
|
|
2172
|
-
return api;
|
|
2173
|
-
};
|
|
2174
|
-
function getTraceParent(_ = null) {
|
|
2175
|
-
try {
|
|
2176
|
-
const { context, propagation } = _loadOtelApi();
|
|
2177
|
-
const carrier = {};
|
|
2178
|
-
propagation.inject(context.active(), carrier, {
|
|
2179
|
-
set: (c, k, v) => {
|
|
2180
|
-
c[k] = v;
|
|
2181
|
-
}
|
|
2182
|
-
});
|
|
2183
|
-
const traceparent = carrier["traceparent"];
|
|
2184
|
-
if (!traceparent) {
|
|
2185
|
-
throw new Error("traceparent header not available from active context");
|
|
2186
|
-
}
|
|
2187
|
-
return traceparent;
|
|
2188
|
-
} catch (error) {
|
|
2189
|
-
throw new Error(
|
|
2190
|
-
`Failed to extract traceparent: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2191
|
-
);
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
2261
|
export {
|
|
2195
2262
|
KeletContext,
|
|
2196
2263
|
KeletProvider,
|
|
2197
2264
|
VoteFeedback,
|
|
2198
|
-
getTraceParent,
|
|
2199
2265
|
useDefaultFeedbackHandler,
|
|
2200
2266
|
useFeedbackState,
|
|
2201
2267
|
useKelet
|