@kelet-ai/feedback-ui 0.6.0 → 0.7.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 CHANGED
@@ -193,6 +193,7 @@ Controls how feedback is collected and what data is captured:
193
193
  - **When to use**: Content editing, user interactions, workflow analysis
194
194
  - **Data captured**: State diffs, interaction patterns, timing data
195
195
  - **User experience**: Seamless, no interruption
196
+ - **Smart filtering**: Ignores common loading patterns (null → data) by default
196
197
 
197
198
  ```tsx
198
199
  // Implicit feedback - tracks changes automatically
@@ -201,6 +202,11 @@ const [content, setContent] = useFeedbackState(
201
202
  'content-editor'
202
203
  );
203
204
  // Sends feedback when user stops editing
205
+
206
+ // Loading pattern - no noise generated
207
+ const [user, setUser] = useFeedbackState(null, 'user-data');
208
+ setUser(userData); // ❌ No feedback sent (ignoreInitialNullish: true)
209
+ setUser(updatedUser); // ✅ Feedback sent for real changes
204
210
  ```
205
211
 
206
212
  ### **🏷️ Trigger Names**
@@ -527,14 +533,15 @@ interface FeedbackData {
527
533
 
528
534
  #### **useFeedbackState Options**
529
535
 
530
- | Option | Type | Default | Description |
531
- | ---------------------- | ------------------------------------ | --------------------- | ----------------------------- |
532
- | `debounceMs` | `number` | `1500` | Debounce time in milliseconds |
533
- | `diffType` | `'git' \| 'object' \| 'json'` | `'git'` | Diff output format |
534
- | `compareWith` | `(a: T, b: T) => boolean` | `undefined` | Custom equality function |
535
- | `metadata` | `Record<string, any>` | `{}` | Additional metadata |
536
- | `vote` | `'upvote' \| 'downvote' \| function` | `auto` | Vote determination logic |
537
- | `default_trigger_name` | `string` | `'auto_state_change'` | Default trigger name |
536
+ | Option | Type | Default | Description |
537
+ | ---------------------- | ------------------------------------ | --------------------- | -------------------------------------- |
538
+ | `debounceMs` | `number` | `1500` | Debounce time in milliseconds |
539
+ | `diffType` | `'git' \| 'object' \| 'json'` | `'git'` | Diff output format |
540
+ | `compareWith` | `(a: T, b: T) => boolean` | `undefined` | Custom equality function |
541
+ | `metadata` | `Record<string, any>` | `{}` | Additional metadata |
542
+ | `vote` | `'upvote' \| 'downvote' \| function` | `auto` | Vote determination logic |
543
+ | `default_trigger_name` | `string` | `'auto_state_change'` | Default trigger name |
544
+ | `ignoreInitialNullish` | `boolean` | `true` | Skip null/undefined → data transitions |
538
545
 
539
546
  ---
540
547
 
@@ -364,6 +364,9 @@ const useKelet = () => {
364
364
  const useDefaultFeedbackHandler = () => {
365
365
  const context = useContext(KeletContext);
366
366
  if (!context) {
367
+ console.warn(
368
+ "No FeedbackHandler found: defaultFeedbackHandler is not possible since there's no KeletProvider wrapping this call, and handler not provided"
369
+ );
367
370
  return async () => {
368
371
  };
369
372
  } else {
@@ -2006,9 +2009,15 @@ function useStateChangeTracking(currentState, tx_id, options) {
2006
2009
  const diffType = options?.diffType ?? "git";
2007
2010
  const compareWith = options?.compareWith;
2008
2011
  const defaultTriggerName = options?.default_trigger_name ?? "auto_state_change";
2012
+ const ignoreInitialNullish = options?.ignoreInitialNullish ?? true;
2009
2013
  const prevStateRef = useRef(currentState);
2010
2014
  const changeStartStateRef = useRef(currentState);
2011
2015
  const isFirstRenderRef = useRef(true);
2016
+ const initialStateRef = useRef(currentState);
2017
+ const hasHadNonNullishStateRef = useRef(
2018
+ currentState != null
2019
+ // != null catches both null and undefined
2020
+ );
2012
2021
  const timeoutRef = useRef(null);
2013
2022
  const currentTriggerNameRef = useRef(void 0);
2014
2023
  const sendFeedback = useCallback(
@@ -2066,6 +2075,17 @@ function useStateChangeTracking(currentState, tx_id, options) {
2066
2075
  const prevState = prevStateRef.current;
2067
2076
  const isEqual = compareWith ? compareWith(prevState, currentState) : JSON.stringify(prevState) === JSON.stringify(currentState);
2068
2077
  if (!isEqual) {
2078
+ const shouldIgnoreChange = ignoreInitialNullish && initialStateRef.current == null && // Initial state was nullish
2079
+ !hasHadNonNullishStateRef.current && // We haven't had non-nullish state before
2080
+ currentState != null;
2081
+ if (currentState != null) {
2082
+ hasHadNonNullishStateRef.current = true;
2083
+ }
2084
+ if (shouldIgnoreChange) {
2085
+ prevStateRef.current = currentState;
2086
+ changeStartStateRef.current = currentState;
2087
+ return;
2088
+ }
2069
2089
  if (!timeoutRef.current) {
2070
2090
  changeStartStateRef.current = prevState;
2071
2091
  }
@@ -2099,6 +2119,7 @@ function useStateChangeTracking(currentState, tx_id, options) {
2099
2119
  debounceMs,
2100
2120
  compareWith,
2101
2121
  defaultTriggerName,
2122
+ ignoreInitialNullish,
2102
2123
  sendFeedback
2103
2124
  ]);
2104
2125
  return {