@kelet-ai/feedback-ui 1.1.3 → 1.2.0
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 +140 -70
- package/dist/components/vote-feedback.d.ts +1 -1
- package/dist/contexts/kelet.d.ts +20 -0
- package/dist/feedback-ui.es.js +270 -263
- package/dist/feedback-ui.es.js.map +1 -1
- package/dist/feedback-ui.es.min.js +850 -838
- package/dist/feedback-ui.es.min.js.map +1 -1
- package/dist/feedback-ui.umd.js +269 -262
- 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/diff-utils.d.ts +1 -1
- package/dist/hooks/feedback-state/types.d.ts +5 -5
- package/dist/index.d.ts +1 -1
- package/dist/types/index.d.ts +16 -9
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -23,16 +23,16 @@ npx shadcn add https://feedback-ui.kelet.ai/r/vote-feedback.json
|
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
|
-
import { ShadcnVoteFeedback } from
|
|
26
|
+
import { ShadcnVoteFeedback } from "@/components/ui/vote-feedback"
|
|
27
27
|
|
|
28
28
|
function App() {
|
|
29
29
|
return (
|
|
30
30
|
<ShadcnVoteFeedback
|
|
31
31
|
session_id="my-feature"
|
|
32
|
-
onFeedback={feedback => console.log(feedback)}
|
|
32
|
+
onFeedback={(feedback) => console.log(feedback)}
|
|
33
33
|
variant="outline"
|
|
34
34
|
/>
|
|
35
|
-
)
|
|
35
|
+
)
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -89,16 +89,16 @@ npm install @kelet-ai/feedback-ui
|
|
|
89
89
|
Users explicitly vote and provide comments:
|
|
90
90
|
|
|
91
91
|
```tsx
|
|
92
|
-
import { VoteFeedback } from
|
|
92
|
+
import { VoteFeedback } from "@kelet-ai/feedback-ui"
|
|
93
93
|
|
|
94
|
-
|
|
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>
|
|
98
98
|
<VoteFeedback.Textarea placeholder="How can we improve?" />
|
|
99
99
|
<VoteFeedback.SubmitButton>Send feedback</VoteFeedback.SubmitButton>
|
|
100
100
|
</VoteFeedback.Popover>
|
|
101
|
-
</VoteFeedback.Root
|
|
101
|
+
</VoteFeedback.Root>
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
### **Pattern 2: Implicit State Tracking**
|
|
@@ -106,42 +106,79 @@ import { VoteFeedback } from '@kelet-ai/feedback-ui';
|
|
|
106
106
|
Capture user behavior automatically by using a drop-in replacement for useState:
|
|
107
107
|
|
|
108
108
|
```tsx
|
|
109
|
-
import { useFeedbackState } from
|
|
109
|
+
import { useFeedbackState } from "@kelet-ai/feedback-ui"
|
|
110
110
|
|
|
111
111
|
function ContentEditor() {
|
|
112
112
|
// Drop-in useState replacement that tracks changes
|
|
113
113
|
const [content, setContent] = useFeedbackState(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
114
|
+
"Start writing...",
|
|
115
|
+
"content-editor"
|
|
116
|
+
)
|
|
117
117
|
|
|
118
118
|
return (
|
|
119
119
|
<textarea
|
|
120
120
|
value={content}
|
|
121
|
-
onChange={e => setContent(e.target.value)}
|
|
121
|
+
onChange={(e) => setContent(e.target.value)}
|
|
122
122
|
placeholder="Edit this content..."
|
|
123
123
|
/>
|
|
124
|
-
)
|
|
124
|
+
)
|
|
125
125
|
// 🎯 Automatically sends feedback when user stops editing!
|
|
126
126
|
}
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### **Pattern 4: Manual Signal Sending**
|
|
130
|
+
|
|
131
|
+
Send signals directly without tying them to component state — useful for custom events, page views, or any interaction not covered by the other patterns:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { KeletProvider, useKeletSignal } from "@kelet-ai/feedback-ui"
|
|
135
|
+
|
|
136
|
+
function CopyButton({ sessionId }: { sessionId: string }) {
|
|
137
|
+
const sendSignal = useKeletSignal()
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<button
|
|
141
|
+
onClick={() => {
|
|
142
|
+
navigator.clipboard.writeText("...")
|
|
143
|
+
sendSignal({
|
|
144
|
+
session_id: sessionId,
|
|
145
|
+
kind: "feedback",
|
|
146
|
+
source: "human",
|
|
147
|
+
score: 1,
|
|
148
|
+
trigger_name: "copy_button_clicked",
|
|
149
|
+
})
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
Copy
|
|
153
|
+
</button>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function App() {
|
|
158
|
+
return (
|
|
159
|
+
<KeletProvider apiKey="..." project="my-project">
|
|
160
|
+
<CopyButton sessionId="response-abc123" />
|
|
161
|
+
</KeletProvider>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
129
166
|
### **Pattern 3: Complex State with Reducer**
|
|
130
167
|
|
|
131
168
|
For advanced state management with automatic trigger tracking, by using a drop-in replacement for useReducer:
|
|
132
169
|
|
|
133
170
|
```tsx
|
|
134
|
-
import { useFeedbackReducer } from
|
|
171
|
+
import { useFeedbackReducer } from "@kelet-ai/feedback-ui"
|
|
135
172
|
|
|
136
173
|
function TodoApp() {
|
|
137
|
-
const [todos, dispatch] = useFeedbackReducer(todoReducer, [],
|
|
174
|
+
const [todos, dispatch] = useFeedbackReducer(todoReducer, [], "todo-app")
|
|
138
175
|
|
|
139
176
|
return (
|
|
140
|
-
<button onClick={() => dispatch({ type:
|
|
177
|
+
<button onClick={() => dispatch({ type: "ADD_TODO", text: "New task" })}>
|
|
141
178
|
Add Todo
|
|
142
179
|
</button>
|
|
143
180
|
// 🎯 Automatically sends feedback with trigger_name: 'ADD_TODO'
|
|
144
|
-
)
|
|
181
|
+
)
|
|
145
182
|
}
|
|
146
183
|
```
|
|
147
184
|
|
|
@@ -198,15 +235,15 @@ Controls how feedback is collected and what data is captured:
|
|
|
198
235
|
```tsx
|
|
199
236
|
// Implicit feedback - tracks changes automatically
|
|
200
237
|
const [content, setContent] = useFeedbackState(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
238
|
+
"Initial content",
|
|
239
|
+
"content-editor"
|
|
240
|
+
)
|
|
204
241
|
// Sends feedback when user stops editing
|
|
205
242
|
|
|
206
243
|
// Loading pattern - no noise generated
|
|
207
|
-
const [user, setUser] = useFeedbackState(null,
|
|
208
|
-
setUser(userData)
|
|
209
|
-
setUser(updatedUser)
|
|
244
|
+
const [user, setUser] = useFeedbackState(null, "user-data")
|
|
245
|
+
setUser(userData) // ❌ No feedback sent (ignoreInitialNullish: true)
|
|
246
|
+
setUser(updatedUser) // ✅ Feedback sent for real changes
|
|
210
247
|
```
|
|
211
248
|
|
|
212
249
|
### **🏷️ Trigger Names**
|
|
@@ -224,16 +261,16 @@ Categorization system for grouping and analyzing feedback:
|
|
|
224
261
|
|
|
225
262
|
```tsx
|
|
226
263
|
// Content creation triggers
|
|
227
|
-
|
|
264
|
+
;"user_typing" | "ai_generation" | "spell_check" | "auto_format"
|
|
228
265
|
|
|
229
266
|
// User interaction triggers
|
|
230
|
-
|
|
267
|
+
;"manual_edit" | "voice_input" | "copy_paste" | "drag_drop"
|
|
231
268
|
|
|
232
269
|
// Workflow triggers
|
|
233
|
-
|
|
270
|
+
;"draft" | "review" | "approval" | "publication"
|
|
234
271
|
|
|
235
272
|
// AI interaction triggers
|
|
236
|
-
|
|
273
|
+
;"ai_completion" | "ai_correction" | "prompt_result"
|
|
237
274
|
```
|
|
238
275
|
|
|
239
276
|
#### **Dynamic Trigger Names**
|
|
@@ -241,14 +278,14 @@ Categorization system for grouping and analyzing feedback:
|
|
|
241
278
|
```tsx
|
|
242
279
|
const [document, setDocument] = useFeedbackState(
|
|
243
280
|
initialDoc,
|
|
244
|
-
|
|
245
|
-
{ default_trigger_name:
|
|
246
|
-
)
|
|
281
|
+
"document-editor",
|
|
282
|
+
{ default_trigger_name: "user_edit" }
|
|
283
|
+
)
|
|
247
284
|
|
|
248
285
|
// Different triggers for different actions
|
|
249
|
-
setDocument(aiGeneratedContent,
|
|
250
|
-
setDocument(userEditedContent,
|
|
251
|
-
setDocument(spellCheckedContent,
|
|
286
|
+
setDocument(aiGeneratedContent, "ai_generation")
|
|
287
|
+
setDocument(userEditedContent, "manual_refinement")
|
|
288
|
+
setDocument(spellCheckedContent, "spell_check")
|
|
252
289
|
```
|
|
253
290
|
|
|
254
291
|
### **🔄 State Change Tracking**
|
|
@@ -260,9 +297,9 @@ For implicit feedback, understanding how state changes are processed:
|
|
|
260
297
|
```tsx
|
|
261
298
|
// User types: "Hello" → "Hello World" → "Hello World!"
|
|
262
299
|
// Only sends ONE feedback after user stops typing
|
|
263
|
-
const [text, setText] = useFeedbackState(
|
|
300
|
+
const [text, setText] = useFeedbackState("", "editor", {
|
|
264
301
|
debounceMs: 3000, // Wait 3s after last change
|
|
265
|
-
})
|
|
302
|
+
})
|
|
266
303
|
```
|
|
267
304
|
|
|
268
305
|
#### **Diff Calculation**
|
|
@@ -281,14 +318,14 @@ Automatic classification of changes:
|
|
|
281
318
|
|
|
282
319
|
```tsx
|
|
283
320
|
// Smart vote logic based on change magnitude
|
|
284
|
-
const [data, setData] = useFeedbackState(initial,
|
|
321
|
+
const [data, setData] = useFeedbackState(initial, "tracker", {
|
|
285
322
|
vote: (before, after, diffPercentage) => {
|
|
286
323
|
// Small changes = refinement (positive)
|
|
287
|
-
if (diffPercentage <= 0.5) return
|
|
324
|
+
if (diffPercentage <= 0.5) return "upvote"
|
|
288
325
|
// Large changes = major revision (might indicate issues)
|
|
289
|
-
return
|
|
326
|
+
return "downvote"
|
|
290
327
|
},
|
|
291
|
-
})
|
|
328
|
+
})
|
|
292
329
|
```
|
|
293
330
|
|
|
294
331
|
### **🎯 Best Practices Summary**
|
|
@@ -330,7 +367,7 @@ Main container component that manages feedback state.
|
|
|
330
367
|
session_id="unique-id" // Required: Unique tracking ID
|
|
331
368
|
onFeedback={handleFeedback} // Required: Callback function
|
|
332
369
|
trigger_name="user_feedback" // Optional: Categorization
|
|
333
|
-
extra_metadata={{ page:
|
|
370
|
+
extra_metadata={{ page: "home" }} // Optional: Additional data
|
|
334
371
|
>
|
|
335
372
|
{/* Child components */}
|
|
336
373
|
</VoteFeedback.Root>
|
|
@@ -359,6 +396,39 @@ Context-aware feedback form that appears after voting.
|
|
|
359
396
|
|
|
360
397
|
---
|
|
361
398
|
|
|
399
|
+
## ✉️ Manual Signal Sending
|
|
400
|
+
|
|
401
|
+
### **useKeletSignal Hook**
|
|
402
|
+
|
|
403
|
+
Returns the signal-sending function from the nearest `KeletProvider`. Use this to send signals manually for any event — custom interactions, page views, feature usage — without tying them to component state.
|
|
404
|
+
|
|
405
|
+
Safe to call outside a provider: returns a no-op and logs a warning.
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { useKeletSignal } from "@kelet-ai/feedback-ui"
|
|
409
|
+
|
|
410
|
+
function MyComponent() {
|
|
411
|
+
const sendSignal = useKeletSignal()
|
|
412
|
+
|
|
413
|
+
const handleAction = () => {
|
|
414
|
+
sendSignal({
|
|
415
|
+
session_id: "my-session",
|
|
416
|
+
kind: "feedback",
|
|
417
|
+
source: "human",
|
|
418
|
+
score: 1,
|
|
419
|
+
trigger_name: "user_action",
|
|
420
|
+
metadata: { context: "sidebar" },
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return <button onClick={handleAction}>Do something</button>
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Requires a `KeletProvider` ancestor to send signals to the API.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
362
432
|
## 📊 Automatic Feedback Hooks
|
|
363
433
|
|
|
364
434
|
### **useFeedbackState Hook**
|
|
@@ -368,39 +438,39 @@ A **drop-in replacement for React's useState** that automatically tracks state c
|
|
|
368
438
|
#### **Basic Usage**
|
|
369
439
|
|
|
370
440
|
```tsx
|
|
371
|
-
const [count, setCount] = useFeedbackState(0,
|
|
441
|
+
const [count, setCount] = useFeedbackState(0, "counter-widget")
|
|
372
442
|
```
|
|
373
443
|
|
|
374
444
|
#### **Advanced Configuration**
|
|
375
445
|
|
|
376
446
|
```tsx
|
|
377
447
|
const [profile, setProfile] = useFeedbackState(
|
|
378
|
-
{ name:
|
|
379
|
-
state => `profile-${state.email}`, // Dynamic session_id
|
|
448
|
+
{ name: "", email: "" },
|
|
449
|
+
(state) => `profile-${state.email}`, // Dynamic session_id
|
|
380
450
|
{
|
|
381
451
|
debounceMs: 2000, // Wait time before sending feedback
|
|
382
|
-
diffType:
|
|
383
|
-
metadata: { component:
|
|
384
|
-
vote:
|
|
452
|
+
diffType: "object", // Format: 'git' | 'object' | 'json'
|
|
453
|
+
metadata: { component: "UserProfile" },
|
|
454
|
+
vote: "upvote", // Static vote or custom function
|
|
385
455
|
}
|
|
386
|
-
)
|
|
456
|
+
)
|
|
387
457
|
```
|
|
388
458
|
|
|
389
459
|
#### **Trigger Names for Categorization**
|
|
390
460
|
|
|
391
461
|
```tsx
|
|
392
462
|
const [content, setContent] = useFeedbackState(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
{ default_trigger_name:
|
|
396
|
-
)
|
|
463
|
+
"Initial content",
|
|
464
|
+
"content-editor",
|
|
465
|
+
{ default_trigger_name: "manual_edit" }
|
|
466
|
+
)
|
|
397
467
|
|
|
398
468
|
// Uses default trigger
|
|
399
|
-
setContent(
|
|
469
|
+
setContent("User typed this")
|
|
400
470
|
|
|
401
471
|
// Override with specific trigger
|
|
402
|
-
setContent(
|
|
403
|
-
setContent(
|
|
472
|
+
setContent("AI generated this", "ai_assistance")
|
|
473
|
+
setContent("Spell checker fixed this", "spell_check")
|
|
404
474
|
```
|
|
405
475
|
|
|
406
476
|
### **useFeedbackReducer Hook**
|
|
@@ -411,12 +481,12 @@ A **drop-in replacement for React's useReducer** with automatic trigger name ext
|
|
|
411
481
|
const [state, dispatch] = useFeedbackReducer(
|
|
412
482
|
counterReducer,
|
|
413
483
|
{ count: 0 },
|
|
414
|
-
|
|
415
|
-
)
|
|
484
|
+
"counter-widget"
|
|
485
|
+
)
|
|
416
486
|
|
|
417
|
-
dispatch({ type:
|
|
418
|
-
dispatch({ type:
|
|
419
|
-
dispatch({ type:
|
|
487
|
+
dispatch({ type: "increment" }) // trigger_name: 'increment'
|
|
488
|
+
dispatch({ type: "reset" }) // trigger_name: 'reset'
|
|
489
|
+
dispatch({ type: "custom" }, "override") // Custom trigger name
|
|
420
490
|
```
|
|
421
491
|
|
|
422
492
|
---
|
|
@@ -426,7 +496,7 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
426
496
|
### **Basic Voting Interface**
|
|
427
497
|
|
|
428
498
|
```tsx
|
|
429
|
-
<VoteFeedback.Root onFeedback={feedback => console.log(feedback)}>
|
|
499
|
+
<VoteFeedback.Root onFeedback={(feedback) => console.log(feedback)}>
|
|
430
500
|
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
431
501
|
<VoteFeedback.DownvoteButton>👎</VoteFeedback.DownvoteButton>
|
|
432
502
|
</VoteFeedback.Root>
|
|
@@ -470,7 +540,7 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
470
540
|
onFeedback={handleFeedback}
|
|
471
541
|
trigger_name="ai_evaluation"
|
|
472
542
|
extra_metadata={{
|
|
473
|
-
model:
|
|
543
|
+
model: "gpt-4",
|
|
474
544
|
prompt_length: 150,
|
|
475
545
|
response_time: 1200,
|
|
476
546
|
}}
|
|
@@ -492,14 +562,14 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
492
562
|
|
|
493
563
|
```typescript
|
|
494
564
|
interface FeedbackData {
|
|
495
|
-
session_id: string
|
|
496
|
-
vote:
|
|
497
|
-
explanation?: string
|
|
498
|
-
extra_metadata?: Record<string, any
|
|
499
|
-
source?:
|
|
500
|
-
correction?: string
|
|
501
|
-
selection?: string
|
|
502
|
-
trigger_name?: string
|
|
565
|
+
session_id: string // Unique tracking ID
|
|
566
|
+
vote: "upvote" | "downvote" // User's vote
|
|
567
|
+
explanation?: string // Optional user comment
|
|
568
|
+
extra_metadata?: Record<string, any> // Additional context data
|
|
569
|
+
source?: "IMPLICIT" | "EXPLICIT" // How feedback was collected
|
|
570
|
+
correction?: string // For implicit feedback diffs
|
|
571
|
+
selection?: string // Selected text context
|
|
572
|
+
trigger_name?: string // Categorization tag
|
|
503
573
|
}
|
|
504
574
|
```
|
|
505
575
|
|
|
@@ -615,9 +685,9 @@ bun run checks # Run all quality checks (lint, format, typecheck, tests)
|
|
|
615
685
|
|
|
616
686
|
```tsx
|
|
617
687
|
// ✅ Ensure debounce time has passed(the time, not the configuration! :P) and state actually changed
|
|
618
|
-
const [value, setValue] = useFeedbackState(
|
|
688
|
+
const [value, setValue] = useFeedbackState("initial", "test", {
|
|
619
689
|
debounceMs: 1000, // Wait 1 second after last change
|
|
620
|
-
})
|
|
690
|
+
})
|
|
621
691
|
```
|
|
622
692
|
|
|
623
693
|
#### **Q: Styling not working**
|
|
@@ -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, session_id: sessionIdProp,
|
|
3
|
+
Root: ({ children, onFeedback, defaultText, session_id: sessionIdProp, metadata, trigger_name: triggerProp, trace_id, }: 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/contexts/kelet.d.ts
CHANGED
|
@@ -12,6 +12,26 @@ interface KeletProviderProps {
|
|
|
12
12
|
}
|
|
13
13
|
export declare const KeletContext: React.Context<KeletContextValue | null>;
|
|
14
14
|
export declare const useKelet: () => KeletContextValue;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the signal-sending function from the nearest KeletProvider.
|
|
17
|
+
*
|
|
18
|
+
* Use this hook to send signals manually — e.g. tracking custom events,
|
|
19
|
+
* page views, or any interaction not covered by useFeedbackState.
|
|
20
|
+
*
|
|
21
|
+
* Safe to call outside a provider: returns a no-op and logs a warning.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const sendSignal = useKeletSignal()
|
|
25
|
+
*
|
|
26
|
+
* sendSignal({
|
|
27
|
+
* session_id: 'my-session',
|
|
28
|
+
* kind: 'feedback',
|
|
29
|
+
* source: 'human',
|
|
30
|
+
* score: 1,
|
|
31
|
+
* trigger_name: 'copy_button_clicked',
|
|
32
|
+
* })
|
|
33
|
+
*/
|
|
34
|
+
export declare const useKeletSignal: () => ((data: FeedbackData) => Promise<void>);
|
|
15
35
|
export declare const useDefaultFeedbackHandler: () => ((data: FeedbackData) => Promise<void>);
|
|
16
36
|
export declare const KeletProvider: React.FC<React.PropsWithChildren<KeletProviderProps>>;
|
|
17
37
|
export {};
|