yaver-feedback-react-native 0.8.0 → 0.8.2
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 +40 -6
- package/dist/AuthOverlay.js +1 -1
- package/dist/FeedbackModal.js +475 -265
- package/dist/P2PClient.d.ts +9 -3
- package/dist/P2PClient.js +30 -2
- package/dist/QuickActionIcon.d.ts +10 -2
- package/dist/QuickActionIcon.js +49 -12
- package/dist/YaverFeedback.d.ts +5 -0
- package/dist/YaverFeedback.js +24 -0
- package/dist/__tests__/P2PClient.test.js +30 -0
- package/dist/__tests__/YaverFeedback.test.js +39 -0
- package/dist/capture.d.ts +11 -0
- package/dist/capture.js +59 -0
- package/dist/index.d.ts +8 -7
- package/dist/index.js +9 -7
- package/dist/preferences.d.ts +11 -0
- package/dist/preferences.js +82 -0
- package/dist/types.d.ts +29 -4
- package/dist/upload.d.ts +1 -0
- package/dist/upload.js +8 -0
- package/package.json +11 -5
- package/src/AuthOverlay.tsx +1 -0
- package/src/FeedbackModal.tsx +561 -319
- package/src/P2PClient.ts +44 -3
- package/src/QuickActionIcon.tsx +76 -13
- package/src/YaverFeedback.ts +31 -0
- package/src/__tests__/P2PClient.test.ts +40 -0
- package/src/__tests__/YaverFeedback.test.ts +42 -0
- package/src/capture.ts +74 -0
- package/src/index.ts +8 -6
- package/src/preferences.ts +96 -0
- package/src/types.ts +29 -4
- package/src/upload.ts +9 -0
package/dist/P2PClient.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ export interface FeedbackEvent {
|
|
|
4
4
|
timestamp: string;
|
|
5
5
|
data: any;
|
|
6
6
|
}
|
|
7
|
+
export interface ReloadAck {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
mode: 'dev' | 'bundle';
|
|
10
|
+
acknowledged: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
nativeChangesDetected?: boolean;
|
|
13
|
+
changeClass?: string;
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* Lightweight P2P HTTP client for communicating with a Yaver agent.
|
|
9
17
|
*
|
|
@@ -69,9 +77,7 @@ export declare class P2PClient {
|
|
|
69
77
|
projectName?: string;
|
|
70
78
|
bundleId?: string;
|
|
71
79
|
projectPath?: string;
|
|
72
|
-
}): Promise<
|
|
73
|
-
ok: boolean;
|
|
74
|
-
}>;
|
|
80
|
+
}): Promise<ReloadAck>;
|
|
75
81
|
/**
|
|
76
82
|
* Open a vibing session on the connected agent. Vibing is the Yaver
|
|
77
83
|
* interactive coding-agent flow — `/vibing/execute` creates a task with
|
package/dist/P2PClient.js
CHANGED
|
@@ -152,6 +152,13 @@ class P2PClient {
|
|
|
152
152
|
name: 'screen_recording.mp4',
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
|
+
if (bundle.audio) {
|
|
156
|
+
formData.append('audio', {
|
|
157
|
+
uri: react_native_1.Platform.OS === 'android' ? `file://${bundle.audio}` : bundle.audio,
|
|
158
|
+
type: 'audio/m4a',
|
|
159
|
+
name: 'voice_note.m4a',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
155
162
|
const response = await fetch(`${this.baseUrl}/feedback`, {
|
|
156
163
|
method: 'POST',
|
|
157
164
|
headers: {
|
|
@@ -284,7 +291,18 @@ class P2PClient {
|
|
|
284
291
|
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
285
292
|
});
|
|
286
293
|
if (primary.ok) {
|
|
287
|
-
|
|
294
|
+
const payload = await primary.json().catch(() => ({}));
|
|
295
|
+
const nativeChangesDetected = payload.nativeChangesDetected === true;
|
|
296
|
+
return {
|
|
297
|
+
ok: true,
|
|
298
|
+
mode: 'dev',
|
|
299
|
+
acknowledged: true,
|
|
300
|
+
nativeChangesDetected,
|
|
301
|
+
changeClass: typeof payload.changeClass === 'string' ? payload.changeClass : undefined,
|
|
302
|
+
message: nativeChangesDetected
|
|
303
|
+
? 'Reload accepted, but native changes need a rebuild.'
|
|
304
|
+
: 'Hot reload request accepted.',
|
|
305
|
+
};
|
|
288
306
|
}
|
|
289
307
|
// Dev mode failed — fall through to bundle rebuild below rather
|
|
290
308
|
// than surfacing the raw error, so the user never has to know
|
|
@@ -314,7 +332,17 @@ class P2PClient {
|
|
|
314
332
|
const text = await res.text().catch(() => '');
|
|
315
333
|
throw new Error(friendlyReloadError(res.status, text));
|
|
316
334
|
}
|
|
317
|
-
|
|
335
|
+
const payload = await res.json().catch(() => ({}));
|
|
336
|
+
return {
|
|
337
|
+
ok: true,
|
|
338
|
+
mode: 'bundle',
|
|
339
|
+
acknowledged: true,
|
|
340
|
+
changeClass: typeof payload.changeClass === 'string' ? payload.changeClass : undefined,
|
|
341
|
+
nativeChangesDetected: payload.nativeChangesDetected === true,
|
|
342
|
+
message: typeof payload.message === 'string' && payload.message.trim()
|
|
343
|
+
? payload.message
|
|
344
|
+
: 'Reload request acknowledged. Agent is rebuilding the bundle.',
|
|
345
|
+
};
|
|
318
346
|
}
|
|
319
347
|
/**
|
|
320
348
|
* Open a vibing session on the connected agent. Vibing is the Yaver
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export interface QuickActionIconProps {
|
|
3
|
-
/**
|
|
3
|
+
/** Deprecated alias for `backgroundColor`. */
|
|
4
4
|
color?: string;
|
|
5
|
+
/** Override the background from FeedbackConfig.quickIconBackgroundColor. */
|
|
6
|
+
backgroundColor?: string;
|
|
7
|
+
/** Override the label color from FeedbackConfig.quickIconForegroundColor. */
|
|
8
|
+
foregroundColor?: string;
|
|
9
|
+
/** Override the border color from FeedbackConfig.quickIconBorderColor. */
|
|
10
|
+
borderColor?: string;
|
|
11
|
+
/** Override the shadow color from FeedbackConfig.quickIconShadowColor. */
|
|
12
|
+
shadowColor?: string;
|
|
5
13
|
/** Override the initial position from FeedbackConfig.quickIconInitialPosition. */
|
|
6
14
|
initialPosition?: {
|
|
7
15
|
x: number;
|
|
@@ -24,7 +32,7 @@ export interface QuickActionIconProps {
|
|
|
24
32
|
* hidden the user can still shake to open feedback.
|
|
25
33
|
*
|
|
26
34
|
* Visibility is controlled by `FeedbackConfig.quickIcon`:
|
|
27
|
-
* - `'auto'` (default) → `'
|
|
35
|
+
* - `'auto'` (default) → `'after-shake'` on iOS/Android, `'off'` on web.
|
|
28
36
|
* - `'always'` → visible from first render.
|
|
29
37
|
* - `'after-shake'` → hidden until `yaverFeedback:firstShake` fires.
|
|
30
38
|
* - `'off'` → never rendered.
|
package/dist/QuickActionIcon.js
CHANGED
|
@@ -50,7 +50,10 @@ function isRunningInsideYaverHost() {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
const DEFAULT_SIZE = 44;
|
|
53
|
-
const
|
|
53
|
+
const DEFAULT_BACKGROUND_COLOR = '#ff6b2c';
|
|
54
|
+
const DEFAULT_LABEL_COLOR = '#111111';
|
|
55
|
+
const DEFAULT_BORDER_COLOR = 'rgba(255,255,255,0.92)';
|
|
56
|
+
const DEFAULT_SHADOW_COLOR = '#000000';
|
|
54
57
|
const LONG_PRESS_MS = 550;
|
|
55
58
|
/**
|
|
56
59
|
* Small tap-to-open icon for the Yaver Feedback SDK.
|
|
@@ -66,7 +69,7 @@ const LONG_PRESS_MS = 550;
|
|
|
66
69
|
* hidden the user can still shake to open feedback.
|
|
67
70
|
*
|
|
68
71
|
* Visibility is controlled by `FeedbackConfig.quickIcon`:
|
|
69
|
-
* - `'auto'` (default) → `'
|
|
72
|
+
* - `'auto'` (default) → `'after-shake'` on iOS/Android, `'off'` on web.
|
|
70
73
|
* - `'always'` → visible from first render.
|
|
71
74
|
* - `'after-shake'` → hidden until `yaverFeedback:firstShake` fires.
|
|
72
75
|
* - `'off'` → never rendered.
|
|
@@ -74,16 +77,29 @@ const LONG_PRESS_MS = 550;
|
|
|
74
77
|
* Suppressed entirely when the SDK is loaded inside Yaver's super-host
|
|
75
78
|
* (the Yaver mobile app owns the shake gesture + overlay in that case).
|
|
76
79
|
*/
|
|
77
|
-
const QuickActionIcon = ({ color: colorProp, initialPosition: initialPositionProp, size = DEFAULT_SIZE, }) => {
|
|
80
|
+
const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorProp, foregroundColor: foregroundColorProp, borderColor: borderColorProp, shadowColor: shadowColorProp, initialPosition: initialPositionProp, size = DEFAULT_SIZE, }) => {
|
|
78
81
|
const config = YaverFeedback_1.YaverFeedback.getConfig();
|
|
79
82
|
const mode = (() => {
|
|
80
83
|
const raw = config?.quickIcon ?? 'auto';
|
|
81
84
|
if (raw === 'auto') {
|
|
82
|
-
return react_native_1.Platform.OS === 'web' ? 'off' : '
|
|
85
|
+
return react_native_1.Platform.OS === 'web' ? 'off' : 'after-shake';
|
|
83
86
|
}
|
|
84
87
|
return raw;
|
|
85
88
|
})();
|
|
86
|
-
const
|
|
89
|
+
const backgroundColor = backgroundColorProp ??
|
|
90
|
+
colorProp ??
|
|
91
|
+
config?.quickIconBackgroundColor ??
|
|
92
|
+
config?.quickIconColor ??
|
|
93
|
+
DEFAULT_BACKGROUND_COLOR;
|
|
94
|
+
const foregroundColor = foregroundColorProp ??
|
|
95
|
+
config?.quickIconForegroundColor ??
|
|
96
|
+
DEFAULT_LABEL_COLOR;
|
|
97
|
+
const borderColor = borderColorProp ??
|
|
98
|
+
config?.quickIconBorderColor ??
|
|
99
|
+
DEFAULT_BORDER_COLOR;
|
|
100
|
+
const shadowColor = shadowColorProp ??
|
|
101
|
+
config?.quickIconShadowColor ??
|
|
102
|
+
DEFAULT_SHADOW_COLOR;
|
|
87
103
|
const { width, height } = react_native_1.Dimensions.get('window');
|
|
88
104
|
const defaultStart = initialPositionProp ??
|
|
89
105
|
config?.quickIconInitialPosition ?? {
|
|
@@ -95,6 +111,7 @@ const QuickActionIcon = ({ color: colorProp, initialPosition: initialPositionPro
|
|
|
95
111
|
const dragStart = (0, react_1.useRef)(null);
|
|
96
112
|
const didDrag = (0, react_1.useRef)(false);
|
|
97
113
|
const [userDisabled, setUserDisabled] = (0, react_1.useState)(null);
|
|
114
|
+
const [colorPreset, setColorPreset] = (0, react_1.useState)(null);
|
|
98
115
|
const [shakenThisSession, setShakenThisSession] = (0, react_1.useState)(false);
|
|
99
116
|
const [menuOpen, setMenuOpen] = (0, react_1.useState)(false);
|
|
100
117
|
const [hostSuppressed] = (0, react_1.useState)(() => isRunningInsideYaverHost());
|
|
@@ -107,6 +124,10 @@ const QuickActionIcon = ({ color: colorProp, initialPosition: initialPositionPro
|
|
|
107
124
|
if (alive)
|
|
108
125
|
setUserDisabled(v);
|
|
109
126
|
});
|
|
127
|
+
(0, preferences_1.getQuickIconColorPreset)().then((v) => {
|
|
128
|
+
if (alive)
|
|
129
|
+
setColorPreset(v);
|
|
130
|
+
});
|
|
110
131
|
return () => {
|
|
111
132
|
alive = false;
|
|
112
133
|
};
|
|
@@ -130,9 +151,15 @@ const QuickActionIcon = ({ color: colorProp, initialPosition: initialPositionPro
|
|
|
130
151
|
void (0, preferences_1.setQuickIconDisabled)(true);
|
|
131
152
|
setMenuOpen(false);
|
|
132
153
|
});
|
|
154
|
+
const colorSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:quickIconColorChange', (next) => {
|
|
155
|
+
const preset = next?.preset ?? null;
|
|
156
|
+
setColorPreset(preset);
|
|
157
|
+
void (0, preferences_1.setQuickIconColorPreset)(preset);
|
|
158
|
+
});
|
|
133
159
|
return () => {
|
|
134
160
|
showSub.remove();
|
|
135
161
|
hideSub.remove();
|
|
162
|
+
colorSub.remove();
|
|
136
163
|
};
|
|
137
164
|
}, []);
|
|
138
165
|
const panResponder = (0, react_1.useRef)(react_native_1.PanResponder.create({
|
|
@@ -188,6 +215,7 @@ const QuickActionIcon = ({ color: colorProp, initialPosition: initialPositionPro
|
|
|
188
215
|
return null;
|
|
189
216
|
if (!YaverFeedback_1.YaverFeedback.isEnabled())
|
|
190
217
|
return null;
|
|
218
|
+
const presetColors = colorPreset ? preferences_1.QUICK_ICON_COLOR_PRESETS[colorPreset] : null;
|
|
191
219
|
const visualSize = size;
|
|
192
220
|
const radius = visualSize / 2;
|
|
193
221
|
return (<react_native_1.Animated.View pointerEvents="box-none" style={[
|
|
@@ -216,11 +244,21 @@ const QuickActionIcon = ({ color: colorProp, initialPosition: initialPositionPro
|
|
|
216
244
|
width: visualSize,
|
|
217
245
|
height: visualSize,
|
|
218
246
|
borderRadius: radius,
|
|
219
|
-
backgroundColor:
|
|
247
|
+
backgroundColor: presetColors?.backgroundColor ?? backgroundColor,
|
|
248
|
+
borderColor: presetColors?.borderColor ?? borderColor,
|
|
249
|
+
shadowColor: presetColors?.shadowColor ?? shadowColor,
|
|
220
250
|
opacity: pressed ? 0.85 : 1,
|
|
221
251
|
},
|
|
222
252
|
]}>
|
|
223
|
-
<react_native_1.Text style={[
|
|
253
|
+
<react_native_1.Text style={[
|
|
254
|
+
styles.iconLabel,
|
|
255
|
+
{
|
|
256
|
+
color: presetColors?.foregroundColor ?? foregroundColor,
|
|
257
|
+
fontSize: Math.round(visualSize * 0.5),
|
|
258
|
+
},
|
|
259
|
+
]}>
|
|
260
|
+
y
|
|
261
|
+
</react_native_1.Text>
|
|
224
262
|
</react_native_1.Pressable>
|
|
225
263
|
{menuOpen ? (<react_native_1.View style={styles.menu}>
|
|
226
264
|
<react_native_1.Pressable onPress={openFeedback} style={({ pressed }) => [
|
|
@@ -253,14 +291,13 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
253
291
|
icon: {
|
|
254
292
|
alignItems: 'center',
|
|
255
293
|
justifyContent: 'center',
|
|
256
|
-
shadowColor: '#000',
|
|
257
294
|
shadowOffset: { width: 0, height: 2 },
|
|
258
|
-
shadowOpacity: 0.
|
|
259
|
-
shadowRadius:
|
|
260
|
-
elevation:
|
|
295
|
+
shadowOpacity: 0.34,
|
|
296
|
+
shadowRadius: 6,
|
|
297
|
+
elevation: 7,
|
|
298
|
+
borderWidth: 2,
|
|
261
299
|
},
|
|
262
300
|
iconLabel: {
|
|
263
|
-
color: '#ffffff',
|
|
264
301
|
fontWeight: '700',
|
|
265
302
|
includeFontPadding: false,
|
|
266
303
|
},
|
package/dist/YaverFeedback.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FeedbackConfig, CapturedError } from './types';
|
|
2
2
|
import { P2PClient } from './P2PClient';
|
|
3
|
+
import { QuickIconColorPreset } from './preferences';
|
|
3
4
|
/**
|
|
4
5
|
* Main entry point for the Yaver Feedback SDK.
|
|
5
6
|
* Call `YaverFeedback.init()` once at app startup.
|
|
@@ -60,6 +61,8 @@ export declare class YaverFeedback {
|
|
|
60
61
|
* newly-selected machine.
|
|
61
62
|
*/
|
|
62
63
|
static setPreferredDevice(deviceId: string): Promise<void>;
|
|
64
|
+
/** Resolve the currently selected remote machine from the authenticated device list. */
|
|
65
|
+
static getSelectedRemoteDevice(): Promise<import("./auth").RemoteDevice | null>;
|
|
63
66
|
/**
|
|
64
67
|
* Sign out: clear cached token + device, tear down the P2P client. The
|
|
65
68
|
* SDK stays enabled; the next feedback trigger will re-prompt for login.
|
|
@@ -223,6 +226,8 @@ export declare class YaverFeedback {
|
|
|
223
226
|
* about the programmatic API.
|
|
224
227
|
*/
|
|
225
228
|
static isQuickIconHidden(): Promise<boolean>;
|
|
229
|
+
static setQuickIconColorPreset(preset: QuickIconColorPreset | null): Promise<void>;
|
|
230
|
+
static getQuickIconColorPreset(): Promise<QuickIconColorPreset | null>;
|
|
226
231
|
/** Clear the persisted "user hid the icon" flag. */
|
|
227
232
|
static resetQuickIconPreference(): Promise<void>;
|
|
228
233
|
/** Tear down the SDK (stop shake detector, clear state). */
|
package/dist/YaverFeedback.js
CHANGED
|
@@ -76,6 +76,7 @@ class YaverFeedback {
|
|
|
76
76
|
autoLogin: true,
|
|
77
77
|
...cfg,
|
|
78
78
|
};
|
|
79
|
+
firstShakeFired = false;
|
|
79
80
|
// Route the in-SDK login screen to prod yaver.io by default; callers may
|
|
80
81
|
// override for staging via authConvexSiteUrl / authWebBaseUrl.
|
|
81
82
|
(0, auth_1.configureAuthEndpoints)({
|
|
@@ -345,6 +346,15 @@ class YaverFeedback {
|
|
|
345
346
|
p2pClient = null;
|
|
346
347
|
await YaverFeedback.discoverAgent();
|
|
347
348
|
}
|
|
349
|
+
/** Resolve the currently selected remote machine from the authenticated device list. */
|
|
350
|
+
static async getSelectedRemoteDevice() {
|
|
351
|
+
if (!config?.authToken || !config.preferredDeviceId)
|
|
352
|
+
return null;
|
|
353
|
+
const preferredDeviceId = config.preferredDeviceId;
|
|
354
|
+
const devices = await (0, auth_1.listReachableDevices)(config.authToken);
|
|
355
|
+
const all = [...devices.owned, ...devices.shared];
|
|
356
|
+
return all.find((device) => device.deviceId === preferredDeviceId) ?? null;
|
|
357
|
+
}
|
|
348
358
|
/**
|
|
349
359
|
* Sign out: clear cached token + device, tear down the P2P client. The
|
|
350
360
|
* SDK stays enabled; the next feedback trigger will re-prompt for login.
|
|
@@ -808,6 +818,19 @@ class YaverFeedback {
|
|
|
808
818
|
static async isQuickIconHidden() {
|
|
809
819
|
return (0, preferences_1.getQuickIconDisabled)();
|
|
810
820
|
}
|
|
821
|
+
static async setQuickIconColorPreset(preset) {
|
|
822
|
+
await (0, preferences_1.setQuickIconColorPreset)(preset);
|
|
823
|
+
try {
|
|
824
|
+
const { DeviceEventEmitter } = require('react-native');
|
|
825
|
+
DeviceEventEmitter.emit('yaverFeedback:quickIconColorChange', { preset });
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
// emitter unavailable — preference is still persisted
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
static async getQuickIconColorPreset() {
|
|
832
|
+
return (0, preferences_1.getQuickIconColorPreset)();
|
|
833
|
+
}
|
|
811
834
|
/** Clear the persisted "user hid the icon" flag. */
|
|
812
835
|
static async resetQuickIconPreference() {
|
|
813
836
|
await YaverFeedback.setQuickIconVisible(true);
|
|
@@ -818,6 +841,7 @@ class YaverFeedback {
|
|
|
818
841
|
shakeDetector.stop();
|
|
819
842
|
shakeDetector = null;
|
|
820
843
|
}
|
|
844
|
+
firstShakeFired = false;
|
|
821
845
|
enabled = false;
|
|
822
846
|
config = null;
|
|
823
847
|
p2pClient = null;
|
|
@@ -166,4 +166,34 @@ describe('P2PClient', () => {
|
|
|
166
166
|
expect(result).toEqual(builds);
|
|
167
167
|
});
|
|
168
168
|
});
|
|
169
|
+
describe('reloadApp()', () => {
|
|
170
|
+
it('returns an acknowledgement for dev reloads', async () => {
|
|
171
|
+
mockFetch.mockResolvedValue({
|
|
172
|
+
ok: true,
|
|
173
|
+
json: () => Promise.resolve({ ok: true, changeClass: 'js_only' }),
|
|
174
|
+
});
|
|
175
|
+
const client = new P2PClient_1.P2PClient('http://localhost:18080', 'tok');
|
|
176
|
+
const result = await client.reloadApp('dev');
|
|
177
|
+
expect(result).toEqual(expect.objectContaining({
|
|
178
|
+
ok: true,
|
|
179
|
+
mode: 'dev',
|
|
180
|
+
acknowledged: true,
|
|
181
|
+
message: 'Hot reload request accepted.',
|
|
182
|
+
}));
|
|
183
|
+
});
|
|
184
|
+
it('returns an acknowledgement for bundle reloads', async () => {
|
|
185
|
+
mockFetch.mockResolvedValue({
|
|
186
|
+
ok: true,
|
|
187
|
+
json: () => Promise.resolve({ ok: true }),
|
|
188
|
+
});
|
|
189
|
+
const client = new P2PClient_1.P2PClient('http://localhost:18080', 'tok');
|
|
190
|
+
const result = await client.reloadApp('bundle');
|
|
191
|
+
expect(result).toEqual(expect.objectContaining({
|
|
192
|
+
ok: true,
|
|
193
|
+
mode: 'bundle',
|
|
194
|
+
acknowledged: true,
|
|
195
|
+
message: 'Reload request acknowledged. Agent is rebuilding the bundle.',
|
|
196
|
+
}));
|
|
197
|
+
});
|
|
198
|
+
});
|
|
169
199
|
});
|
|
@@ -16,6 +16,33 @@ jest.mock('../Discovery', () => ({
|
|
|
16
16
|
discover: jest.fn(),
|
|
17
17
|
},
|
|
18
18
|
}));
|
|
19
|
+
jest.mock('../auth', () => ({
|
|
20
|
+
configureAuthEndpoints: jest.fn(),
|
|
21
|
+
setStrictNativeAuth: jest.fn(),
|
|
22
|
+
getToken: jest.fn(async () => null),
|
|
23
|
+
getSelectedDeviceId: jest.fn(async () => null),
|
|
24
|
+
clearToken: jest.fn(async () => { }),
|
|
25
|
+
clearSelectedDeviceId: jest.fn(async () => { }),
|
|
26
|
+
listReachableDevices: jest.fn(async () => ({
|
|
27
|
+
owned: [
|
|
28
|
+
{
|
|
29
|
+
deviceId: 'device-1',
|
|
30
|
+
name: 'Dev Mac',
|
|
31
|
+
platform: 'darwin',
|
|
32
|
+
isOnline: true,
|
|
33
|
+
needsAuth: false,
|
|
34
|
+
runnerDown: false,
|
|
35
|
+
lastHeartbeat: Date.now(),
|
|
36
|
+
isGuest: false,
|
|
37
|
+
accessScope: 'owner',
|
|
38
|
+
quicHost: '127.0.0.1',
|
|
39
|
+
quicPort: 18080,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
shared: [],
|
|
43
|
+
})),
|
|
44
|
+
DEFAULT_CONVEX_SITE_URL: 'https://example.convex.site',
|
|
45
|
+
}));
|
|
19
46
|
// Reset module-level state between tests by re-requiring
|
|
20
47
|
beforeEach(() => {
|
|
21
48
|
// YaverFeedback uses module-level variables (config, enabled, p2pClient).
|
|
@@ -106,6 +133,18 @@ describe('YaverFeedback', () => {
|
|
|
106
133
|
expect(cfg.agentUrl).toBe('http://10.0.0.1:18080');
|
|
107
134
|
});
|
|
108
135
|
});
|
|
136
|
+
describe('getSelectedRemoteDevice()', () => {
|
|
137
|
+
it('returns the selected device from the reachable device list', async () => {
|
|
138
|
+
YaverFeedback_1.YaverFeedback.init({
|
|
139
|
+
authToken: 'tok',
|
|
140
|
+
preferredDeviceId: 'device-1',
|
|
141
|
+
enabled: true,
|
|
142
|
+
});
|
|
143
|
+
const device = await YaverFeedback_1.YaverFeedback.getSelectedRemoteDevice();
|
|
144
|
+
expect(device?.deviceId).toBe('device-1');
|
|
145
|
+
expect(device?.name).toBe('Dev Mac');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
109
148
|
describe('startReport()', () => {
|
|
110
149
|
it('does nothing when not enabled', async () => {
|
|
111
150
|
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok', enabled: false });
|
package/dist/capture.d.ts
CHANGED
|
@@ -22,6 +22,17 @@
|
|
|
22
22
|
* modal. See `FeedbackModal.handleScreenshotForFix`.
|
|
23
23
|
*/
|
|
24
24
|
export declare function captureScreenshot(): Promise<string>;
|
|
25
|
+
export interface PickedFeedbackFile {
|
|
26
|
+
path: string;
|
|
27
|
+
name: string;
|
|
28
|
+
mimeType?: string;
|
|
29
|
+
kind: 'image' | 'video' | 'audio' | 'unknown';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pick an existing media file from the device. Requires
|
|
33
|
+
* `expo-document-picker` to be installed.
|
|
34
|
+
*/
|
|
35
|
+
export declare function pickFeedbackFile(): Promise<PickedFeedbackFile>;
|
|
25
36
|
/**
|
|
26
37
|
* Start a screen-recording session. Requires
|
|
27
38
|
* `react-native-record-screen` as a peer dep.
|
package/dist/capture.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.captureScreenshot = captureScreenshot;
|
|
19
|
+
exports.pickFeedbackFile = pickFeedbackFile;
|
|
19
20
|
exports.startVideoRecording = startVideoRecording;
|
|
20
21
|
exports.stopVideoRecording = stopVideoRecording;
|
|
21
22
|
exports.isVideoRecording = isVideoRecording;
|
|
@@ -45,6 +46,64 @@ async function captureScreenshot() {
|
|
|
45
46
|
String(err));
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
function classifyPickedFile(name, mimeType) {
|
|
50
|
+
const lowerName = name.toLowerCase();
|
|
51
|
+
const lowerMime = (mimeType ?? '').toLowerCase();
|
|
52
|
+
if (lowerMime.startsWith('image/') ||
|
|
53
|
+
lowerName.endsWith('.png') ||
|
|
54
|
+
lowerName.endsWith('.jpg') ||
|
|
55
|
+
lowerName.endsWith('.jpeg') ||
|
|
56
|
+
lowerName.endsWith('.webp')) {
|
|
57
|
+
return 'image';
|
|
58
|
+
}
|
|
59
|
+
if (lowerMime.startsWith('video/') ||
|
|
60
|
+
lowerName.endsWith('.mp4') ||
|
|
61
|
+
lowerName.endsWith('.mov') ||
|
|
62
|
+
lowerName.endsWith('.m4v')) {
|
|
63
|
+
return 'video';
|
|
64
|
+
}
|
|
65
|
+
if (lowerMime.startsWith('audio/') ||
|
|
66
|
+
lowerName.endsWith('.m4a') ||
|
|
67
|
+
lowerName.endsWith('.aac') ||
|
|
68
|
+
lowerName.endsWith('.wav') ||
|
|
69
|
+
lowerName.endsWith('.mp3')) {
|
|
70
|
+
return 'audio';
|
|
71
|
+
}
|
|
72
|
+
return 'unknown';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Pick an existing media file from the device. Requires
|
|
76
|
+
* `expo-document-picker` to be installed.
|
|
77
|
+
*/
|
|
78
|
+
async function pickFeedbackFile() {
|
|
79
|
+
try {
|
|
80
|
+
const picker = require('expo-document-picker');
|
|
81
|
+
const result = await picker.getDocumentAsync({
|
|
82
|
+
copyToCacheDirectory: true,
|
|
83
|
+
multiple: false,
|
|
84
|
+
type: ['image/*', 'video/*', 'audio/*'],
|
|
85
|
+
});
|
|
86
|
+
if (result?.canceled) {
|
|
87
|
+
throw new Error('File selection canceled.');
|
|
88
|
+
}
|
|
89
|
+
const asset = result?.assets?.[0];
|
|
90
|
+
if (!asset?.uri) {
|
|
91
|
+
throw new Error('No file selected.');
|
|
92
|
+
}
|
|
93
|
+
const name = asset.name || asset.uri.split('/').pop() || 'attachment';
|
|
94
|
+
const mimeType = asset.mimeType;
|
|
95
|
+
return {
|
|
96
|
+
path: asset.uri,
|
|
97
|
+
name,
|
|
98
|
+
mimeType,
|
|
99
|
+
kind: classifyPickedFile(name, mimeType),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw new Error('[YaverFeedback] File upload needs `expo-document-picker` as an optional peer dependency. ' +
|
|
104
|
+
String(err));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
48
107
|
let videoRecorderModule = null;
|
|
49
108
|
let videoRecordingActive = false;
|
|
50
109
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* yaver-feedback-react-native — Visual feedback SDK for Yaver.
|
|
3
3
|
*
|
|
4
|
-
* Shake-to-report surface with
|
|
4
|
+
* Shake-to-report surface with three launch actions:
|
|
5
5
|
* 1. Hot Reload — instant JS reload
|
|
6
|
-
* 2.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* 2. Vibing — open a vibing session on the agent
|
|
7
|
+
* 3. Screenshot & Fix — capture the current screen and trigger
|
|
8
|
+
* the fix loop
|
|
9
|
+
*
|
|
10
|
+
* The small quick-access icon stays hidden until the first shake by
|
|
11
|
+
* default on mobile, then remains available unless the user hides it.
|
|
11
12
|
*
|
|
12
13
|
* @example
|
|
13
14
|
* ```tsx
|
|
@@ -50,7 +51,7 @@ export { FixReport } from './FixReport';
|
|
|
50
51
|
export { getQuickIconDisabled, setQuickIconDisabled, clearQuickIconDisabled, } from './preferences';
|
|
51
52
|
export { configureAuthEndpoints, getConvexSiteUrl, getWebBaseUrl, getToken, saveToken, clearToken, getUser, saveUser, getSelectedDeviceId, saveSelectedDeviceId, clearSelectedDeviceId, validateToken, signInWithApple, signInWithOAuth, signupWithEmail, loginWithEmail, listReachableDevices, DEFAULT_CONVEX_SITE_URL, DEFAULT_WEB_BASE_URL, DEFAULT_OAUTH_REDIRECT, } from './auth';
|
|
52
53
|
export type { OAuthProvider, User, RemoteDevice, DeviceList, } from './auth';
|
|
53
|
-
export { captureScreenshot, startVideoRecording, stopVideoRecording, isVideoRecording, } from './capture';
|
|
54
|
+
export { captureScreenshot, pickFeedbackFile, startVideoRecording, stopVideoRecording, isVideoRecording, } from './capture';
|
|
54
55
|
export { uploadFeedback } from './upload';
|
|
55
56
|
export type { FeedbackConfig, FeedbackBundle, FeedbackMetadata, DeviceInfo, AppInfo, TimelineEvent, FeedbackReport, FeedbackStreamEvent, VoiceCapability, CapturedError, TestFix, TestSession, } from './types';
|
|
56
57
|
export type { BlackBoxEvent, BlackBoxConfig, BlackBoxCommand, CommandHandler } from './BlackBox';
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* yaver-feedback-react-native — Visual feedback SDK for Yaver.
|
|
4
4
|
*
|
|
5
|
-
* Shake-to-report surface with
|
|
5
|
+
* Shake-to-report surface with three launch actions:
|
|
6
6
|
* 1. Hot Reload — instant JS reload
|
|
7
|
-
* 2.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* 2. Vibing — open a vibing session on the agent
|
|
8
|
+
* 3. Screenshot & Fix — capture the current screen and trigger
|
|
9
|
+
* the fix loop
|
|
10
|
+
*
|
|
11
|
+
* The small quick-access icon stays hidden until the first shake by
|
|
12
|
+
* default on mobile, then remains available unless the user hides it.
|
|
12
13
|
*
|
|
13
14
|
* @example
|
|
14
15
|
* ```tsx
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
* ```
|
|
29
30
|
*/
|
|
30
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.uploadFeedback = exports.isVideoRecording = exports.stopVideoRecording = exports.startVideoRecording = exports.captureScreenshot = exports.DEFAULT_OAUTH_REDIRECT = exports.DEFAULT_WEB_BASE_URL = exports.DEFAULT_CONVEX_SITE_URL = exports.listReachableDevices = exports.loginWithEmail = exports.signupWithEmail = exports.signInWithOAuth = exports.signInWithApple = exports.validateToken = exports.clearSelectedDeviceId = exports.saveSelectedDeviceId = exports.getSelectedDeviceId = exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.FeedbackModal = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.YaverFeedback = void 0;
|
|
32
|
+
exports.uploadFeedback = exports.isVideoRecording = exports.stopVideoRecording = exports.startVideoRecording = exports.pickFeedbackFile = exports.captureScreenshot = exports.DEFAULT_OAUTH_REDIRECT = exports.DEFAULT_WEB_BASE_URL = exports.DEFAULT_CONVEX_SITE_URL = exports.listReachableDevices = exports.loginWithEmail = exports.signupWithEmail = exports.signInWithOAuth = exports.signInWithApple = exports.validateToken = exports.clearSelectedDeviceId = exports.saveSelectedDeviceId = exports.getSelectedDeviceId = exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.FeedbackModal = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.YaverFeedback = void 0;
|
|
32
33
|
var YaverFeedback_1 = require("./YaverFeedback");
|
|
33
34
|
Object.defineProperty(exports, "YaverFeedback", { enumerable: true, get: function () { return YaverFeedback_1.YaverFeedback; } });
|
|
34
35
|
var BlackBox_1 = require("./BlackBox");
|
|
@@ -88,6 +89,7 @@ Object.defineProperty(exports, "DEFAULT_WEB_BASE_URL", { enumerable: true, get:
|
|
|
88
89
|
Object.defineProperty(exports, "DEFAULT_OAUTH_REDIRECT", { enumerable: true, get: function () { return auth_1.DEFAULT_OAUTH_REDIRECT; } });
|
|
89
90
|
var capture_1 = require("./capture");
|
|
90
91
|
Object.defineProperty(exports, "captureScreenshot", { enumerable: true, get: function () { return capture_1.captureScreenshot; } });
|
|
92
|
+
Object.defineProperty(exports, "pickFeedbackFile", { enumerable: true, get: function () { return capture_1.pickFeedbackFile; } });
|
|
91
93
|
Object.defineProperty(exports, "startVideoRecording", { enumerable: true, get: function () { return capture_1.startVideoRecording; } });
|
|
92
94
|
Object.defineProperty(exports, "stopVideoRecording", { enumerable: true, get: function () { return capture_1.stopVideoRecording; } });
|
|
93
95
|
Object.defineProperty(exports, "isVideoRecording", { enumerable: true, get: function () { return capture_1.isVideoRecording; } });
|
package/dist/preferences.d.ts
CHANGED
|
@@ -12,7 +12,18 @@
|
|
|
12
12
|
* still works (it just can't remember the disable beyond the
|
|
13
13
|
* in-memory session).
|
|
14
14
|
*/
|
|
15
|
+
export type QuickIconColorPreset = 'orange' | 'lime' | 'cyan' | 'pink' | 'yellow' | 'slate';
|
|
16
|
+
export declare const QUICK_ICON_COLOR_PRESETS: Record<QuickIconColorPreset, {
|
|
17
|
+
label: string;
|
|
18
|
+
backgroundColor: string;
|
|
19
|
+
foregroundColor: string;
|
|
20
|
+
borderColor: string;
|
|
21
|
+
shadowColor: string;
|
|
22
|
+
}>;
|
|
15
23
|
/** True if the user has long-pressed the icon and chosen "Hide". */
|
|
16
24
|
export declare function getQuickIconDisabled(): Promise<boolean>;
|
|
17
25
|
export declare function setQuickIconDisabled(disabled: boolean): Promise<void>;
|
|
18
26
|
export declare function clearQuickIconDisabled(): Promise<void>;
|
|
27
|
+
export declare function getQuickIconColorPreset(): Promise<QuickIconColorPreset | null>;
|
|
28
|
+
export declare function setQuickIconColorPreset(preset: QuickIconColorPreset | null): Promise<void>;
|
|
29
|
+
export declare function clearQuickIconColorPreset(): Promise<void>;
|