@kelet-ai/feedback-ui 1.1.1 → 1.1.4
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 +70 -70
- package/dist/components/vote-feedback.d.ts +1 -1
- package/dist/feedback-ui.es.js +275 -264
- package/dist/feedback-ui.es.js.map +1 -1
- package/dist/feedback-ui.es.min.js +808 -795
- package/dist/feedback-ui.es.min.js.map +1 -1
- package/dist/feedback-ui.umd.js +275 -264
- 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/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,22 +106,22 @@ 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
|
```
|
|
@@ -131,17 +131,17 @@ function ContentEditor() {
|
|
|
131
131
|
For advanced state management with automatic trigger tracking, by using a drop-in replacement for useReducer:
|
|
132
132
|
|
|
133
133
|
```tsx
|
|
134
|
-
import { useFeedbackReducer } from
|
|
134
|
+
import { useFeedbackReducer } from "@kelet-ai/feedback-ui"
|
|
135
135
|
|
|
136
136
|
function TodoApp() {
|
|
137
|
-
const [todos, dispatch] = useFeedbackReducer(todoReducer, [],
|
|
137
|
+
const [todos, dispatch] = useFeedbackReducer(todoReducer, [], "todo-app")
|
|
138
138
|
|
|
139
139
|
return (
|
|
140
|
-
<button onClick={() => dispatch({ type:
|
|
140
|
+
<button onClick={() => dispatch({ type: "ADD_TODO", text: "New task" })}>
|
|
141
141
|
Add Todo
|
|
142
142
|
</button>
|
|
143
143
|
// 🎯 Automatically sends feedback with trigger_name: 'ADD_TODO'
|
|
144
|
-
)
|
|
144
|
+
)
|
|
145
145
|
}
|
|
146
146
|
```
|
|
147
147
|
|
|
@@ -198,15 +198,15 @@ Controls how feedback is collected and what data is captured:
|
|
|
198
198
|
```tsx
|
|
199
199
|
// Implicit feedback - tracks changes automatically
|
|
200
200
|
const [content, setContent] = useFeedbackState(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
201
|
+
"Initial content",
|
|
202
|
+
"content-editor"
|
|
203
|
+
)
|
|
204
204
|
// Sends feedback when user stops editing
|
|
205
205
|
|
|
206
206
|
// Loading pattern - no noise generated
|
|
207
|
-
const [user, setUser] = useFeedbackState(null,
|
|
208
|
-
setUser(userData)
|
|
209
|
-
setUser(updatedUser)
|
|
207
|
+
const [user, setUser] = useFeedbackState(null, "user-data")
|
|
208
|
+
setUser(userData) // ❌ No feedback sent (ignoreInitialNullish: true)
|
|
209
|
+
setUser(updatedUser) // ✅ Feedback sent for real changes
|
|
210
210
|
```
|
|
211
211
|
|
|
212
212
|
### **🏷️ Trigger Names**
|
|
@@ -224,16 +224,16 @@ Categorization system for grouping and analyzing feedback:
|
|
|
224
224
|
|
|
225
225
|
```tsx
|
|
226
226
|
// Content creation triggers
|
|
227
|
-
|
|
227
|
+
;"user_typing" | "ai_generation" | "spell_check" | "auto_format"
|
|
228
228
|
|
|
229
229
|
// User interaction triggers
|
|
230
|
-
|
|
230
|
+
;"manual_edit" | "voice_input" | "copy_paste" | "drag_drop"
|
|
231
231
|
|
|
232
232
|
// Workflow triggers
|
|
233
|
-
|
|
233
|
+
;"draft" | "review" | "approval" | "publication"
|
|
234
234
|
|
|
235
235
|
// AI interaction triggers
|
|
236
|
-
|
|
236
|
+
;"ai_completion" | "ai_correction" | "prompt_result"
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
#### **Dynamic Trigger Names**
|
|
@@ -241,14 +241,14 @@ Categorization system for grouping and analyzing feedback:
|
|
|
241
241
|
```tsx
|
|
242
242
|
const [document, setDocument] = useFeedbackState(
|
|
243
243
|
initialDoc,
|
|
244
|
-
|
|
245
|
-
{ default_trigger_name:
|
|
246
|
-
)
|
|
244
|
+
"document-editor",
|
|
245
|
+
{ default_trigger_name: "user_edit" }
|
|
246
|
+
)
|
|
247
247
|
|
|
248
248
|
// Different triggers for different actions
|
|
249
|
-
setDocument(aiGeneratedContent,
|
|
250
|
-
setDocument(userEditedContent,
|
|
251
|
-
setDocument(spellCheckedContent,
|
|
249
|
+
setDocument(aiGeneratedContent, "ai_generation")
|
|
250
|
+
setDocument(userEditedContent, "manual_refinement")
|
|
251
|
+
setDocument(spellCheckedContent, "spell_check")
|
|
252
252
|
```
|
|
253
253
|
|
|
254
254
|
### **🔄 State Change Tracking**
|
|
@@ -260,9 +260,9 @@ For implicit feedback, understanding how state changes are processed:
|
|
|
260
260
|
```tsx
|
|
261
261
|
// User types: "Hello" → "Hello World" → "Hello World!"
|
|
262
262
|
// Only sends ONE feedback after user stops typing
|
|
263
|
-
const [text, setText] = useFeedbackState(
|
|
263
|
+
const [text, setText] = useFeedbackState("", "editor", {
|
|
264
264
|
debounceMs: 3000, // Wait 3s after last change
|
|
265
|
-
})
|
|
265
|
+
})
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
#### **Diff Calculation**
|
|
@@ -281,14 +281,14 @@ Automatic classification of changes:
|
|
|
281
281
|
|
|
282
282
|
```tsx
|
|
283
283
|
// Smart vote logic based on change magnitude
|
|
284
|
-
const [data, setData] = useFeedbackState(initial,
|
|
284
|
+
const [data, setData] = useFeedbackState(initial, "tracker", {
|
|
285
285
|
vote: (before, after, diffPercentage) => {
|
|
286
286
|
// Small changes = refinement (positive)
|
|
287
|
-
if (diffPercentage <= 0.5) return
|
|
287
|
+
if (diffPercentage <= 0.5) return "upvote"
|
|
288
288
|
// Large changes = major revision (might indicate issues)
|
|
289
|
-
return
|
|
289
|
+
return "downvote"
|
|
290
290
|
},
|
|
291
|
-
})
|
|
291
|
+
})
|
|
292
292
|
```
|
|
293
293
|
|
|
294
294
|
### **🎯 Best Practices Summary**
|
|
@@ -330,7 +330,7 @@ Main container component that manages feedback state.
|
|
|
330
330
|
session_id="unique-id" // Required: Unique tracking ID
|
|
331
331
|
onFeedback={handleFeedback} // Required: Callback function
|
|
332
332
|
trigger_name="user_feedback" // Optional: Categorization
|
|
333
|
-
extra_metadata={{ page:
|
|
333
|
+
extra_metadata={{ page: "home" }} // Optional: Additional data
|
|
334
334
|
>
|
|
335
335
|
{/* Child components */}
|
|
336
336
|
</VoteFeedback.Root>
|
|
@@ -368,39 +368,39 @@ A **drop-in replacement for React's useState** that automatically tracks state c
|
|
|
368
368
|
#### **Basic Usage**
|
|
369
369
|
|
|
370
370
|
```tsx
|
|
371
|
-
const [count, setCount] = useFeedbackState(0,
|
|
371
|
+
const [count, setCount] = useFeedbackState(0, "counter-widget")
|
|
372
372
|
```
|
|
373
373
|
|
|
374
374
|
#### **Advanced Configuration**
|
|
375
375
|
|
|
376
376
|
```tsx
|
|
377
377
|
const [profile, setProfile] = useFeedbackState(
|
|
378
|
-
{ name:
|
|
379
|
-
state => `profile-${state.email}`, // Dynamic session_id
|
|
378
|
+
{ name: "", email: "" },
|
|
379
|
+
(state) => `profile-${state.email}`, // Dynamic session_id
|
|
380
380
|
{
|
|
381
381
|
debounceMs: 2000, // Wait time before sending feedback
|
|
382
|
-
diffType:
|
|
383
|
-
metadata: { component:
|
|
384
|
-
vote:
|
|
382
|
+
diffType: "object", // Format: 'git' | 'object' | 'json'
|
|
383
|
+
metadata: { component: "UserProfile" },
|
|
384
|
+
vote: "upvote", // Static vote or custom function
|
|
385
385
|
}
|
|
386
|
-
)
|
|
386
|
+
)
|
|
387
387
|
```
|
|
388
388
|
|
|
389
389
|
#### **Trigger Names for Categorization**
|
|
390
390
|
|
|
391
391
|
```tsx
|
|
392
392
|
const [content, setContent] = useFeedbackState(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
{ default_trigger_name:
|
|
396
|
-
)
|
|
393
|
+
"Initial content",
|
|
394
|
+
"content-editor",
|
|
395
|
+
{ default_trigger_name: "manual_edit" }
|
|
396
|
+
)
|
|
397
397
|
|
|
398
398
|
// Uses default trigger
|
|
399
|
-
setContent(
|
|
399
|
+
setContent("User typed this")
|
|
400
400
|
|
|
401
401
|
// Override with specific trigger
|
|
402
|
-
setContent(
|
|
403
|
-
setContent(
|
|
402
|
+
setContent("AI generated this", "ai_assistance")
|
|
403
|
+
setContent("Spell checker fixed this", "spell_check")
|
|
404
404
|
```
|
|
405
405
|
|
|
406
406
|
### **useFeedbackReducer Hook**
|
|
@@ -411,12 +411,12 @@ A **drop-in replacement for React's useReducer** with automatic trigger name ext
|
|
|
411
411
|
const [state, dispatch] = useFeedbackReducer(
|
|
412
412
|
counterReducer,
|
|
413
413
|
{ count: 0 },
|
|
414
|
-
|
|
415
|
-
)
|
|
414
|
+
"counter-widget"
|
|
415
|
+
)
|
|
416
416
|
|
|
417
|
-
dispatch({ type:
|
|
418
|
-
dispatch({ type:
|
|
419
|
-
dispatch({ type:
|
|
417
|
+
dispatch({ type: "increment" }) // trigger_name: 'increment'
|
|
418
|
+
dispatch({ type: "reset" }) // trigger_name: 'reset'
|
|
419
|
+
dispatch({ type: "custom" }, "override") // Custom trigger name
|
|
420
420
|
```
|
|
421
421
|
|
|
422
422
|
---
|
|
@@ -426,7 +426,7 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
426
426
|
### **Basic Voting Interface**
|
|
427
427
|
|
|
428
428
|
```tsx
|
|
429
|
-
<VoteFeedback.Root onFeedback={feedback => console.log(feedback)}>
|
|
429
|
+
<VoteFeedback.Root onFeedback={(feedback) => console.log(feedback)}>
|
|
430
430
|
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
431
431
|
<VoteFeedback.DownvoteButton>👎</VoteFeedback.DownvoteButton>
|
|
432
432
|
</VoteFeedback.Root>
|
|
@@ -470,7 +470,7 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
470
470
|
onFeedback={handleFeedback}
|
|
471
471
|
trigger_name="ai_evaluation"
|
|
472
472
|
extra_metadata={{
|
|
473
|
-
model:
|
|
473
|
+
model: "gpt-4",
|
|
474
474
|
prompt_length: 150,
|
|
475
475
|
response_time: 1200,
|
|
476
476
|
}}
|
|
@@ -492,14 +492,14 @@ dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
|
492
492
|
|
|
493
493
|
```typescript
|
|
494
494
|
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
|
|
495
|
+
session_id: string // Unique tracking ID
|
|
496
|
+
vote: "upvote" | "downvote" // User's vote
|
|
497
|
+
explanation?: string // Optional user comment
|
|
498
|
+
extra_metadata?: Record<string, any> // Additional context data
|
|
499
|
+
source?: "IMPLICIT" | "EXPLICIT" // How feedback was collected
|
|
500
|
+
correction?: string // For implicit feedback diffs
|
|
501
|
+
selection?: string // Selected text context
|
|
502
|
+
trigger_name?: string // Categorization tag
|
|
503
503
|
}
|
|
504
504
|
```
|
|
505
505
|
|
|
@@ -615,9 +615,9 @@ bun run checks # Run all quality checks (lint, format, typecheck, tests)
|
|
|
615
615
|
|
|
616
616
|
```tsx
|
|
617
617
|
// ✅ Ensure debounce time has passed(the time, not the configuration! :P) and state actually changed
|
|
618
|
-
const [value, setValue] = useFeedbackState(
|
|
618
|
+
const [value, setValue] = useFeedbackState("initial", "test", {
|
|
619
619
|
debounceMs: 1000, // Wait 1 second after last change
|
|
620
|
-
})
|
|
620
|
+
})
|
|
621
621
|
```
|
|
622
622
|
|
|
623
623
|
#### **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;
|