yaver-feedback-react-native 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/src/P2PClient.ts CHANGED
@@ -79,14 +79,6 @@ export class P2PClient {
79
79
  } as any);
80
80
  }
81
81
 
82
- if (bundle.audio) {
83
- formData.append('audio', {
84
- uri: Platform.OS === 'android' ? `file://${bundle.audio}` : bundle.audio,
85
- type: 'audio/m4a',
86
- name: 'voice_note.m4a',
87
- } as any);
88
- }
89
-
90
82
  if (bundle.video) {
91
83
  formData.append('video', {
92
84
  uri: Platform.OS === 'android' ? `file://${bundle.video}` : bundle.video,
@@ -223,20 +215,82 @@ export class P2PClient {
223
215
  * @param mode - "dev" for hot reload, "bundle" for native bundle rebuild
224
216
  */
225
217
  async reloadApp(mode: 'dev' | 'bundle' = 'dev'): Promise<{ ok: boolean }> {
226
- const response = await fetch(`${this.baseUrl}/dev/reload-app`, {
218
+ // Primary path: /dev/reload — same endpoint the Yaver mobile app uses.
219
+ // Triggers Metro/Expo HMR synchronously and emits an SSE `reload` event
220
+ // on /dev/events that the FeedbackModal can subscribe to for progress.
221
+ //
222
+ // Only fall back to /dev/reload-app (the BlackBox-SSE-broadcast path)
223
+ // when the primary path reports "no dev server running" — that mode is
224
+ // really for the mobile app remotely kicking a third-party app, not
225
+ // for the app kicking itself.
226
+ const primary = await fetch(`${this.baseUrl}/dev/reload`, {
227
+ method: 'POST',
228
+ headers: { Authorization: `Bearer ${this.authToken}` },
229
+ });
230
+ if (primary.ok) {
231
+ return primary.json().catch(() => ({ ok: true }));
232
+ }
233
+ if (primary.status >= 500 || primary.status === 404 || mode === 'bundle') {
234
+ const fallback = await fetch(`${this.baseUrl}/dev/reload-app`, {
235
+ method: 'POST',
236
+ headers: {
237
+ Authorization: `Bearer ${this.authToken}`,
238
+ 'Content-Type': 'application/json',
239
+ },
240
+ body: JSON.stringify({ mode }),
241
+ });
242
+ if (!fallback.ok) {
243
+ const text = await fallback.text().catch(() => '');
244
+ throw new Error(`[P2PClient] Reload failed (${fallback.status}): ${text}`);
245
+ }
246
+ return fallback.json().catch(() => ({ ok: true }));
247
+ }
248
+ const text = await primary.text().catch(() => '');
249
+ throw new Error(`[P2PClient] Reload failed (${primary.status}): ${text}`);
250
+ }
251
+
252
+ /**
253
+ * Open a vibing session on the connected agent. Vibing is the Yaver
254
+ * interactive coding-agent flow — `/vibing/execute` creates a task with
255
+ * the project context plus the user's prompt. Returns the task id the
256
+ * caller can poll via `/tasks/{id}` if needed.
257
+ *
258
+ * Requires an owner/CLI/paired token — the `/vibing*` routes do not
259
+ * currently accept SDK-minted tokens. Power users typically drive
260
+ * vibing from Claude Code / the Yaver mobile app; this method is a
261
+ * convenience for the SDK's one-tap bug-report-to-vibing path.
262
+ */
263
+ async vibing(prompt: string, projectPath?: string): Promise<{ taskId: string }> {
264
+ const response = await fetch(`${this.baseUrl}/vibing/execute`, {
227
265
  method: 'POST',
228
266
  headers: {
229
267
  Authorization: `Bearer ${this.authToken}`,
230
268
  'Content-Type': 'application/json',
231
269
  },
232
- body: JSON.stringify({ mode }),
270
+ body: JSON.stringify({ prompt, projectPath: projectPath ?? '' }),
233
271
  });
234
-
235
272
  if (!response.ok) {
236
273
  const text = await response.text().catch(() => '');
237
- throw new Error(`[P2PClient] Reload app failed (${response.status}): ${text}`);
274
+ throw new Error(`[P2PClient] Vibing failed (${response.status}): ${text}`);
238
275
  }
276
+ return response.json();
277
+ }
239
278
 
279
+ /**
280
+ * After uploading a feedback bundle with `uploadFeedback`, call this
281
+ * with the returned report id to create a fix task on the agent. The
282
+ * task includes the feedback's screenshots, errors, and (when available)
283
+ * the BlackBox context for the originating device.
284
+ */
285
+ async triggerFix(feedbackId: string): Promise<{ taskId: string; prompt: string }> {
286
+ const response = await fetch(`${this.baseUrl}/feedback/${encodeURIComponent(feedbackId)}/fix`, {
287
+ method: 'POST',
288
+ headers: { Authorization: `Bearer ${this.authToken}` },
289
+ });
290
+ if (!response.ok) {
291
+ const text = await response.text().catch(() => '');
292
+ throw new Error(`[P2PClient] Fix trigger failed (${response.status}): ${text}`);
293
+ }
240
294
  return response.json();
241
295
  }
242
296
 
@@ -6,6 +6,7 @@ import { P2PClient } from './P2PClient';
6
6
  import { ShakeDetector } from './ShakeDetector';
7
7
  import {
8
8
  configureAuthEndpoints,
9
+ setStrictNativeAuth,
9
10
  getToken,
10
11
  getSelectedDeviceId,
11
12
  clearToken,
@@ -76,8 +77,6 @@ export class YaverFeedback {
76
77
  config = {
77
78
  trigger: 'shake',
78
79
  maxRecordingDuration: 120,
79
- feedbackMode: 'batch',
80
- agentCommentaryLevel: 0,
81
80
  autoLogin: true,
82
81
  ...cfg,
83
82
  };
@@ -88,6 +87,9 @@ export class YaverFeedback {
88
87
  convexSiteUrl: cfg.authConvexSiteUrl,
89
88
  webBaseUrl: cfg.authWebBaseUrl,
90
89
  });
90
+ // Compile-time lockdown: refuse any browser-hop / device-code fallback
91
+ // and force ASWebAuthenticationSession in ephemeral mode for OAuth.
92
+ setStrictNativeAuth(cfg.strictNativeAuth === true);
91
93
  // If no explicit convexUrl was set but we have an auth site URL, use it
92
94
  // so Discovery.discoverFromConvex() has somewhere to look up the user's
93
95
  // machines (works for both LAN-direct and off-LAN relay paths).
@@ -484,16 +486,6 @@ export class YaverFeedback {
484
486
  return p2pClient;
485
487
  }
486
488
 
487
- /** Returns the current feedback mode. */
488
- static getFeedbackMode(): 'live' | 'narrated' | 'batch' {
489
- return config?.feedbackMode ?? 'batch';
490
- }
491
-
492
- /** Returns the agent commentary level (0-10). */
493
- static getCommentaryLevel(): number {
494
- return config?.agentCommentaryLevel ?? 0;
495
- }
496
-
497
489
  // ─── One-stop SaaS replacement methods ─────────────────────────
498
490
  //
499
491
  // These are the three solo-dev SaaS-replacement entry points
@@ -39,8 +39,6 @@ describe('YaverFeedback', () => {
39
39
  expect(cfg!.agentUrl).toBe('http://localhost:18080');
40
40
  expect(cfg!.trigger).toBe('shake');
41
41
  expect(cfg!.maxRecordingDuration).toBe(120);
42
- expect(cfg!.feedbackMode).toBe('batch');
43
- expect(cfg!.agentCommentaryLevel).toBe(0);
44
42
  });
45
43
 
46
44
  it('respects user-provided values over defaults', () => {
@@ -48,15 +46,13 @@ describe('YaverFeedback', () => {
48
46
  authToken: 'tok',
49
47
  trigger: 'floating-button',
50
48
  maxRecordingDuration: 60,
51
- feedbackMode: 'live',
52
- agentCommentaryLevel: 7,
49
+ strictNativeAuth: true,
53
50
  });
54
51
 
55
52
  const cfg = YaverFeedback.getConfig();
56
53
  expect(cfg!.trigger).toBe('floating-button');
57
54
  expect(cfg!.maxRecordingDuration).toBe(60);
58
- expect(cfg!.feedbackMode).toBe('live');
59
- expect(cfg!.agentCommentaryLevel).toBe(7);
55
+ expect(cfg!.strictNativeAuth).toBe(true);
60
56
  });
61
57
 
62
58
  it('with enabled=false sets enabled to false', () => {
@@ -130,41 +126,6 @@ describe('YaverFeedback', () => {
130
126
  });
131
127
  });
132
128
 
133
- describe('getFeedbackMode()', () => {
134
- it('defaults to batch when no config', () => {
135
- // After any init, feedbackMode defaults to 'batch'
136
- YaverFeedback.init({ authToken: 'tok' });
137
- expect(YaverFeedback.getFeedbackMode()).toBe('batch');
138
- });
139
-
140
- it('returns configured mode', () => {
141
- YaverFeedback.init({ authToken: 'tok', feedbackMode: 'narrated' });
142
- expect(YaverFeedback.getFeedbackMode()).toBe('narrated');
143
- });
144
-
145
- it('returns live when configured', () => {
146
- YaverFeedback.init({ authToken: 'tok', feedbackMode: 'live' });
147
- expect(YaverFeedback.getFeedbackMode()).toBe('live');
148
- });
149
- });
150
-
151
- describe('getCommentaryLevel()', () => {
152
- it('defaults to 0', () => {
153
- YaverFeedback.init({ authToken: 'tok' });
154
- expect(YaverFeedback.getCommentaryLevel()).toBe(0);
155
- });
156
-
157
- it('returns configured level', () => {
158
- YaverFeedback.init({ authToken: 'tok', agentCommentaryLevel: 5 });
159
- expect(YaverFeedback.getCommentaryLevel()).toBe(5);
160
- });
161
-
162
- it('returns max level when set to 10', () => {
163
- YaverFeedback.init({ authToken: 'tok', agentCommentaryLevel: 10 });
164
- expect(YaverFeedback.getCommentaryLevel()).toBe(10);
165
- });
166
- });
167
-
168
129
  describe('startReport()', () => {
169
130
  it('does nothing when not enabled', async () => {
170
131
  YaverFeedback.init({ authToken: 'tok', enabled: false });
@@ -6,7 +6,6 @@ import type {
6
6
  DeviceInfo,
7
7
  AppInfo,
8
8
  FeedbackReport,
9
- AgentCommentary,
10
9
  FeedbackStreamEvent,
11
10
  } from '../types';
12
11
 
@@ -21,8 +20,6 @@ describe('React Native SDK types', () => {
21
20
  expect(config.trigger).toBeUndefined();
22
21
  expect(config.enabled).toBeUndefined();
23
22
  expect(config.maxRecordingDuration).toBeUndefined();
24
- expect(config.feedbackMode).toBeUndefined();
25
- expect(config.agentCommentaryLevel).toBeUndefined();
26
23
  });
27
24
 
28
25
  it('can be constructed with all optional fields', () => {
@@ -32,12 +29,10 @@ describe('React Native SDK types', () => {
32
29
  trigger: 'shake',
33
30
  enabled: true,
34
31
  maxRecordingDuration: 60,
35
- feedbackMode: 'live',
36
- agentCommentaryLevel: 7,
32
+ strictNativeAuth: true,
37
33
  };
38
34
  expect(config.trigger).toBe('shake');
39
- expect(config.feedbackMode).toBe('live');
40
- expect(config.agentCommentaryLevel).toBe(7);
35
+ expect(config.strictNativeAuth).toBe(true);
41
36
  });
42
37
 
43
38
  it('accepts all trigger types', () => {
@@ -47,14 +42,6 @@ describe('React Native SDK types', () => {
47
42
  expect(config.trigger).toBe(trigger);
48
43
  });
49
44
  });
50
-
51
- it('accepts all feedback modes', () => {
52
- const modes: FeedbackConfig['feedbackMode'][] = ['live', 'narrated', 'batch'];
53
- modes.forEach((mode) => {
54
- const config: FeedbackConfig = { authToken: 'tok', feedbackMode: mode };
55
- expect(config.feedbackMode).toBe(mode);
56
- });
57
- });
58
45
  });
59
46
 
60
47
  describe('FeedbackBundle', () => {
@@ -82,10 +69,9 @@ describe('React Native SDK types', () => {
82
69
  expect(bundle.metadata.device.platform).toBe('ios');
83
70
  expect(bundle.screenshots).toEqual([]);
84
71
  expect(bundle.video).toBeUndefined();
85
- expect(bundle.audio).toBeUndefined();
86
72
  });
87
73
 
88
- it('can include optional video, audio, and screenshots', () => {
74
+ it('can include optional video + screenshots', () => {
89
75
  const bundle: FeedbackBundle = {
90
76
  metadata: {
91
77
  timestamp: '2026-03-24T12:00:00Z',
@@ -100,12 +86,10 @@ describe('React Native SDK types', () => {
100
86
  userNote: 'This button does not work',
101
87
  },
102
88
  video: '/tmp/recording.mp4',
103
- audio: '/tmp/voice.m4a',
104
89
  screenshots: ['/tmp/ss1.png', '/tmp/ss2.png'],
105
90
  };
106
91
 
107
92
  expect(bundle.video).toBe('/tmp/recording.mp4');
108
- expect(bundle.audio).toBe('/tmp/voice.m4a');
109
93
  expect(bundle.screenshots).toHaveLength(2);
110
94
  expect(bundle.metadata.userNote).toBe('This button does not work');
111
95
  });
@@ -213,26 +197,6 @@ describe('React Native SDK types', () => {
213
197
  });
214
198
  });
215
199
 
216
- describe('AgentCommentary', () => {
217
- it('has correct structure', () => {
218
- const commentary: AgentCommentary = {
219
- id: 'cmt-1',
220
- timestamp: '2026-03-24T12:00:00Z',
221
- message: 'I see a layout issue on the login screen',
222
- type: 'observation',
223
- };
224
- expect(commentary.type).toBe('observation');
225
- });
226
-
227
- it('accepts all commentary types', () => {
228
- const types: AgentCommentary['type'][] = ['observation', 'suggestion', 'question', 'action'];
229
- types.forEach((type) => {
230
- const c: AgentCommentary = { id: '1', timestamp: 'now', message: 'test', type };
231
- expect(c.type).toBe(type);
232
- });
233
- });
234
- });
235
-
236
200
  describe('FeedbackStreamEvent', () => {
237
201
  it('has correct structure', () => {
238
202
  const event: FeedbackStreamEvent = {
package/src/auth.ts CHANGED
@@ -73,6 +73,7 @@ export const DEFAULT_WEB_BASE_URL = 'https://yaver.io';
73
73
 
74
74
  let convexSiteUrl = DEFAULT_CONVEX_SITE_URL;
75
75
  let webBaseUrl = DEFAULT_WEB_BASE_URL;
76
+ let strictNativeAuth = false;
76
77
 
77
78
  /** Override the Convex site URL + web base (staging vs prod). */
78
79
  export function configureAuthEndpoints(opts: {
@@ -83,6 +84,19 @@ export function configureAuthEndpoints(opts: {
83
84
  if (opts.webBaseUrl) webBaseUrl = opts.webBaseUrl;
84
85
  }
85
86
 
87
+ /**
88
+ * Enable strict native auth: refuse any fallback that would redirect the
89
+ * user to an external browser (Safari / Chrome) or show a device code.
90
+ * See FeedbackConfig.strictNativeAuth for rationale.
91
+ */
92
+ export function setStrictNativeAuth(enabled: boolean): void {
93
+ strictNativeAuth = enabled;
94
+ }
95
+
96
+ export function isStrictNativeAuth(): boolean {
97
+ return strictNativeAuth;
98
+ }
99
+
86
100
  export function getConvexSiteUrl(): string {
87
101
  return convexSiteUrl;
88
102
  }
@@ -291,17 +305,29 @@ export async function signInWithOAuth(
291
305
  opts?: { redirectUrl?: string; preferEphemeralSession?: boolean },
292
306
  ): Promise<{ token: string }> {
293
307
  if (!WebBrowser) {
308
+ // In strictNativeAuth we hard-fail rather than letting the caller
309
+ // fall back to any homegrown `Linking.openURL(…)` flow that would
310
+ // leave the app for Safari. Without strict mode we still can't
311
+ // proceed (no browser module available) so the behavior is the same
312
+ // — just a clearer error message.
294
313
  throw new Error(
295
- 'expo-web-browser is not installed. Add it as a peer dep to enable OAuth sign-in.',
314
+ 'expo-web-browser is not installed. Add it as a peer dep to enable in-app OAuth sign-in.',
296
315
  );
297
316
  }
298
317
  const redirectUrl = opts?.redirectUrl ?? DEFAULT_OAUTH_REDIRECT;
299
318
  const params = new URLSearchParams({ client: 'mobile' });
300
319
  const authUrl = `${webBaseUrl}/api/auth/oauth/${provider}?${params.toString()}`;
301
320
 
321
+ // In strict mode force ephemeral session (ASWebAuthenticationSession
322
+ // with no shared cookie jar) so the OAuth dance is visibly native and
323
+ // can never hand off to the user's default browser.
324
+ const prefer =
325
+ strictNativeAuth || opts?.preferEphemeralSession
326
+ ? true
327
+ : false;
302
328
  const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUrl, {
303
- preferEphemeralSession: opts?.preferEphemeralSession ?? false,
304
- showInRecents: true,
329
+ preferEphemeralSession: prefer,
330
+ showInRecents: !strictNativeAuth,
305
331
  });
306
332
 
307
333
  if (result.type !== 'success' || !result.url) {
package/src/capture.ts CHANGED
@@ -1,18 +1,24 @@
1
1
  /**
2
- * Screen capture and audio recording helpers.
2
+ * Screen capture helpers screenshot + video recording.
3
3
  *
4
- * Screenshot capture requires `react-native-view-shot` as a peer dependency.
5
- * Audio recording requires `react-native-audio-recorder-player` or a
6
- * similar library the implementation below uses a minimal approach
7
- * that works when one of those is available.
4
+ * Peer deps (all optional loaded lazily):
5
+ * - `react-native-view-shot` screenshot
6
+ * - `react-native-record-screen` video recording (iOS ReplayKit /
7
+ * Android MediaProjection)
8
+ *
9
+ * Each helper surfaces a clear error if the module is missing so a host
10
+ * app knows exactly which peer dep to add. Audio-note / voice-command
11
+ * recording was removed in 0.7.0 — see FeedbackModal for the new
12
+ * 5-button surface.
8
13
  */
9
14
 
10
- let audioRecorderModule: any = null;
11
-
12
15
  /**
13
16
  * Capture the current screen as a PNG image.
14
17
  * Requires `react-native-view-shot` to be installed.
15
- * @returns File path of the captured screenshot.
18
+ *
19
+ * Note: the feedback modal should hide itself *before* calling this so the
20
+ * screenshot contains the underlying app state (the actual bug), not the
21
+ * modal. See `FeedbackModal.handleScreenshotForFix`.
16
22
  */
17
23
  export async function captureScreenshot(): Promise<string> {
18
24
  try {
@@ -24,61 +30,91 @@ export async function captureScreenshot(): Promise<string> {
24
30
  return uri;
25
31
  } catch (err) {
26
32
  throw new Error(
27
- '[YaverFeedback] Screenshot capture failed. Make sure react-native-view-shot is installed. ' +
33
+ '[YaverFeedback] Screenshot capture failed. Install react-native-view-shot as a peer dep. ' +
28
34
  String(err),
29
35
  );
30
36
  }
31
37
  }
32
38
 
39
+ let videoRecorderModule: any = null;
40
+ let videoRecordingActive = false;
41
+
33
42
  /**
34
- * Start recording an audio voice note.
35
- * Requires `react-native-audio-recorder-player` to be installed.
43
+ * Start a screen-recording session. Requires
44
+ * `react-native-record-screen` as a peer dep.
45
+ *
46
+ * The user must grant the iOS ReplayKit / Android MediaProjection
47
+ * permission the first time; the prompt is shown by the native module,
48
+ * not the SDK.
36
49
  */
37
- export async function startAudioRecording(): Promise<void> {
50
+ export async function startVideoRecording(): Promise<void> {
51
+ if (videoRecordingActive) {
52
+ throw new Error('[YaverFeedback] A video recording is already in progress.');
53
+ }
38
54
  try {
39
- const AudioRecorderPlayer =
40
- require('react-native-audio-recorder-player').default;
41
- audioRecorderModule = new AudioRecorderPlayer();
42
- await audioRecorderModule.startRecorder();
55
+ videoRecorderModule = require('react-native-record-screen').default ??
56
+ require('react-native-record-screen');
57
+ if (typeof videoRecorderModule.startRecording !== 'function') {
58
+ throw new Error('react-native-record-screen missing startRecording()');
59
+ }
60
+ const result = await videoRecorderModule.startRecording({
61
+ mic: false,
62
+ width: 720,
63
+ bitrate: 1024 * 1000,
64
+ });
65
+ if (result && result.status && result.status !== 'success') {
66
+ throw new Error(`startRecording returned ${result.status}`);
67
+ }
68
+ videoRecordingActive = true;
43
69
  } catch (err) {
44
- audioRecorderModule = null;
70
+ videoRecorderModule = null;
71
+ videoRecordingActive = false;
45
72
  throw new Error(
46
- '[YaverFeedback] Audio recording failed to start. Make sure react-native-audio-recorder-player is installed. ' +
73
+ '[YaverFeedback] Could not start screen recording. Install react-native-record-screen. ' +
47
74
  String(err),
48
75
  );
49
76
  }
50
77
  }
51
78
 
52
79
  /**
53
- * Stop the current audio recording.
54
- * @returns Object with the file path and duration in seconds.
80
+ * Stop the current video recording and return the on-device file path.
55
81
  */
56
- export async function stopAudioRecording(): Promise<{
82
+ export async function stopVideoRecording(): Promise<{
57
83
  path: string;
58
84
  duration: number;
59
85
  }> {
60
- if (!audioRecorderModule) {
61
- throw new Error('[YaverFeedback] No audio recording in progress.');
86
+ if (!videoRecordingActive || !videoRecorderModule) {
87
+ throw new Error('[YaverFeedback] No video recording in progress.');
62
88
  }
63
-
64
89
  try {
65
- const result = await audioRecorderModule.stopRecorder();
66
- const recorder = audioRecorderModule;
67
- audioRecorderModule = null;
68
-
69
- // result is the file path on most implementations
70
- const path = typeof result === 'string' ? result : result?.uri ?? '';
71
- // Duration tracking — recorder-player provides currentPosition in ms
90
+ const res = await videoRecorderModule.stopRecording();
91
+ videoRecordingActive = false;
92
+ const path =
93
+ typeof res === 'string'
94
+ ? res
95
+ : (res?.result?.outputURL as string) ??
96
+ (res?.outputURL as string) ??
97
+ (res?.uri as string) ??
98
+ '';
72
99
  const durationMs =
73
- typeof recorder.currentPosition === 'number'
74
- ? recorder.currentPosition
75
- : 0;
76
-
100
+ typeof res?.result?.duration === 'number'
101
+ ? res.result.duration
102
+ : typeof res?.duration === 'number'
103
+ ? res.duration
104
+ : 0;
105
+ if (!path) {
106
+ throw new Error('stopRecording() returned no file path');
107
+ }
77
108
  return { path, duration: durationMs / 1000 };
78
109
  } catch (err) {
79
- audioRecorderModule = null;
110
+ videoRecordingActive = false;
80
111
  throw new Error(
81
- '[YaverFeedback] Failed to stop audio recording. ' + String(err),
112
+ '[YaverFeedback] Failed to stop screen recording. ' + String(err),
82
113
  );
83
114
  }
84
115
  }
116
+
117
+ /** Whether a video recording is currently active. */
118
+ export function isVideoRecording(): boolean {
119
+ return videoRecordingActive;
120
+ }
package/src/expo.ts CHANGED
@@ -31,7 +31,6 @@ import type { FeedbackConfig } from './types';
31
31
  *
32
32
  * Defaults:
33
33
  * - trigger: 'shake'
34
- * - feedbackMode: 'batch'
35
34
  * - enabled: __DEV__ (only active in development)
36
35
  *
37
36
  * @param overrides - Optional partial config to override defaults
@@ -54,7 +53,6 @@ export function initExpo(overrides?: Partial<FeedbackConfig>): void {
54
53
  YaverFeedback.init({
55
54
  authToken: '', // LAN auto-discovery doesn't require a token
56
55
  trigger: 'shake',
57
- feedbackMode: 'batch',
58
56
  enabled: __DEV__,
59
57
  ...overrides,
60
58
  ...(agentUrl ? { agentUrl } : {}),
package/src/index.ts CHANGED
@@ -1,24 +1,29 @@
1
1
  /**
2
- * @yaver/feedback-react-native — Visual feedback SDK for Yaver.
2
+ * yaver-feedback-react-native — Visual feedback SDK for Yaver.
3
3
  *
4
- * Shake-to-report, screenshots, voice annotations, P2P connection,
5
- * device discovery, and live/narrated/batch feedback modes for vibe coding.
4
+ * Shake-to-report surface with five one-tap actions:
5
+ * 1. Hot Reload — instant JS reload
6
+ * 2. Screenshot & Fix — capture the screen under the modal and
7
+ * kick a fix task on the agent
8
+ * 3. Vibing — open a vibing session on the agent
9
+ * 4. Start / Stop Recording — screen recording toggle
10
+ * 5. Send Video — submit the last recording
6
11
  *
7
12
  * @example
8
13
  * ```tsx
9
- * import { YaverFeedback, FeedbackProvider } from '@yaver/feedback-react-native';
14
+ * import { YaverFeedback, FeedbackModal } from 'yaver-feedback-react-native';
10
15
  *
11
16
  * YaverFeedback.init({
12
17
  * agentUrl: 'http://192.168.1.10:18080',
13
18
  * authToken: 'your-token',
14
19
  * trigger: 'shake',
15
- * feedbackMode: 'live',
20
+ * strictNativeAuth: true,
16
21
  * });
17
22
  *
18
- * // Wrap your app root:
19
- * <FeedbackProvider>
23
+ * <>
20
24
  * <App />
21
- * </FeedbackProvider>
25
+ * <FeedbackModal />
26
+ * </>
22
27
  * ```
23
28
  */
24
29
 
@@ -67,7 +72,12 @@ export type {
67
72
  RemoteDevice,
68
73
  DeviceList,
69
74
  } from './auth';
70
- export { captureScreenshot, startAudioRecording, stopAudioRecording } from './capture';
75
+ export {
76
+ captureScreenshot,
77
+ startVideoRecording,
78
+ stopVideoRecording,
79
+ isVideoRecording,
80
+ } from './capture';
71
81
  export { uploadFeedback } from './upload';
72
82
  export type {
73
83
  FeedbackConfig,
@@ -77,7 +87,6 @@ export type {
77
87
  AppInfo,
78
88
  TimelineEvent,
79
89
  FeedbackReport,
80
- AgentCommentary,
81
90
  FeedbackStreamEvent,
82
91
  VoiceCapability,
83
92
  CapturedError,