yaver-feedback-react-native 0.6.1 → 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/dist/FeedbackModal.d.ts +0 -9
- package/dist/FeedbackModal.js +265 -473
- package/dist/P2PClient.d.ts +24 -0
- package/dist/P2PClient.js +65 -10
- package/dist/YaverFeedback.d.ts +0 -4
- package/dist/YaverFeedback.js +0 -10
- package/dist/__tests__/YaverFeedback.test.js +2 -35
- package/dist/__tests__/types.test.js +3 -35
- package/dist/capture.d.ts +25 -12
- package/dist/capture.js +73 -34
- package/dist/expo.d.ts +0 -1
- package/dist/expo.js +0 -2
- package/dist/index.d.ts +15 -10
- package/dist/index.js +17 -11
- package/dist/types.d.ts +2 -32
- package/dist/upload.d.ts +8 -3
- package/dist/upload.js +5 -12
- package/package.json +1 -1
- package/src/FeedbackModal.tsx +384 -565
- package/src/P2PClient.ts +66 -12
- package/src/YaverFeedback.ts +0 -12
- package/src/__tests__/YaverFeedback.test.ts +2 -41
- package/src/__tests__/types.test.ts +3 -39
- package/src/capture.ts +73 -37
- package/src/expo.ts +0 -2
- package/src/index.ts +19 -10
- package/src/types.ts +2 -33
- package/src/upload.ts +6 -15
package/dist/P2PClient.d.ts
CHANGED
|
@@ -68,6 +68,30 @@ export declare class P2PClient {
|
|
|
68
68
|
reloadApp(mode?: 'dev' | 'bundle'): Promise<{
|
|
69
69
|
ok: boolean;
|
|
70
70
|
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Open a vibing session on the connected agent. Vibing is the Yaver
|
|
73
|
+
* interactive coding-agent flow — `/vibing/execute` creates a task with
|
|
74
|
+
* the project context plus the user's prompt. Returns the task id the
|
|
75
|
+
* caller can poll via `/tasks/{id}` if needed.
|
|
76
|
+
*
|
|
77
|
+
* Requires an owner/CLI/paired token — the `/vibing*` routes do not
|
|
78
|
+
* currently accept SDK-minted tokens. Power users typically drive
|
|
79
|
+
* vibing from Claude Code / the Yaver mobile app; this method is a
|
|
80
|
+
* convenience for the SDK's one-tap bug-report-to-vibing path.
|
|
81
|
+
*/
|
|
82
|
+
vibing(prompt: string, projectPath?: string): Promise<{
|
|
83
|
+
taskId: string;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* After uploading a feedback bundle with `uploadFeedback`, call this
|
|
87
|
+
* with the returned report id to create a fix task on the agent. The
|
|
88
|
+
* task includes the feedback's screenshots, errors, and (when available)
|
|
89
|
+
* the BlackBox context for the originating device.
|
|
90
|
+
*/
|
|
91
|
+
triggerFix(feedbackId: string): Promise<{
|
|
92
|
+
taskId: string;
|
|
93
|
+
prompt: string;
|
|
94
|
+
}>;
|
|
71
95
|
/** Get the download URL for a build artifact. */
|
|
72
96
|
getArtifactUrl(buildId: string): string;
|
|
73
97
|
/**
|
package/dist/P2PClient.js
CHANGED
|
@@ -62,13 +62,6 @@ class P2PClient {
|
|
|
62
62
|
name: `screenshot_${i}.png`,
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
-
if (bundle.audio) {
|
|
66
|
-
formData.append('audio', {
|
|
67
|
-
uri: react_native_1.Platform.OS === 'android' ? `file://${bundle.audio}` : bundle.audio,
|
|
68
|
-
type: 'audio/m4a',
|
|
69
|
-
name: 'voice_note.m4a',
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
65
|
if (bundle.video) {
|
|
73
66
|
formData.append('video', {
|
|
74
67
|
uri: react_native_1.Platform.OS === 'android' ? `file://${bundle.video}` : bundle.video,
|
|
@@ -188,17 +181,79 @@ class P2PClient {
|
|
|
188
181
|
* @param mode - "dev" for hot reload, "bundle" for native bundle rebuild
|
|
189
182
|
*/
|
|
190
183
|
async reloadApp(mode = 'dev') {
|
|
191
|
-
|
|
184
|
+
// Primary path: /dev/reload — same endpoint the Yaver mobile app uses.
|
|
185
|
+
// Triggers Metro/Expo HMR synchronously and emits an SSE `reload` event
|
|
186
|
+
// on /dev/events that the FeedbackModal can subscribe to for progress.
|
|
187
|
+
//
|
|
188
|
+
// Only fall back to /dev/reload-app (the BlackBox-SSE-broadcast path)
|
|
189
|
+
// when the primary path reports "no dev server running" — that mode is
|
|
190
|
+
// really for the mobile app remotely kicking a third-party app, not
|
|
191
|
+
// for the app kicking itself.
|
|
192
|
+
const primary = await fetch(`${this.baseUrl}/dev/reload`, {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
195
|
+
});
|
|
196
|
+
if (primary.ok) {
|
|
197
|
+
return primary.json().catch(() => ({ ok: true }));
|
|
198
|
+
}
|
|
199
|
+
if (primary.status >= 500 || primary.status === 404 || mode === 'bundle') {
|
|
200
|
+
const fallback = await fetch(`${this.baseUrl}/dev/reload-app`, {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
headers: {
|
|
203
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
204
|
+
'Content-Type': 'application/json',
|
|
205
|
+
},
|
|
206
|
+
body: JSON.stringify({ mode }),
|
|
207
|
+
});
|
|
208
|
+
if (!fallback.ok) {
|
|
209
|
+
const text = await fallback.text().catch(() => '');
|
|
210
|
+
throw new Error(`[P2PClient] Reload failed (${fallback.status}): ${text}`);
|
|
211
|
+
}
|
|
212
|
+
return fallback.json().catch(() => ({ ok: true }));
|
|
213
|
+
}
|
|
214
|
+
const text = await primary.text().catch(() => '');
|
|
215
|
+
throw new Error(`[P2PClient] Reload failed (${primary.status}): ${text}`);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Open a vibing session on the connected agent. Vibing is the Yaver
|
|
219
|
+
* interactive coding-agent flow — `/vibing/execute` creates a task with
|
|
220
|
+
* the project context plus the user's prompt. Returns the task id the
|
|
221
|
+
* caller can poll via `/tasks/{id}` if needed.
|
|
222
|
+
*
|
|
223
|
+
* Requires an owner/CLI/paired token — the `/vibing*` routes do not
|
|
224
|
+
* currently accept SDK-minted tokens. Power users typically drive
|
|
225
|
+
* vibing from Claude Code / the Yaver mobile app; this method is a
|
|
226
|
+
* convenience for the SDK's one-tap bug-report-to-vibing path.
|
|
227
|
+
*/
|
|
228
|
+
async vibing(prompt, projectPath) {
|
|
229
|
+
const response = await fetch(`${this.baseUrl}/vibing/execute`, {
|
|
192
230
|
method: 'POST',
|
|
193
231
|
headers: {
|
|
194
232
|
Authorization: `Bearer ${this.authToken}`,
|
|
195
233
|
'Content-Type': 'application/json',
|
|
196
234
|
},
|
|
197
|
-
body: JSON.stringify({
|
|
235
|
+
body: JSON.stringify({ prompt, projectPath: projectPath ?? '' }),
|
|
236
|
+
});
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const text = await response.text().catch(() => '');
|
|
239
|
+
throw new Error(`[P2PClient] Vibing failed (${response.status}): ${text}`);
|
|
240
|
+
}
|
|
241
|
+
return response.json();
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* After uploading a feedback bundle with `uploadFeedback`, call this
|
|
245
|
+
* with the returned report id to create a fix task on the agent. The
|
|
246
|
+
* task includes the feedback's screenshots, errors, and (when available)
|
|
247
|
+
* the BlackBox context for the originating device.
|
|
248
|
+
*/
|
|
249
|
+
async triggerFix(feedbackId) {
|
|
250
|
+
const response = await fetch(`${this.baseUrl}/feedback/${encodeURIComponent(feedbackId)}/fix`, {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
198
253
|
});
|
|
199
254
|
if (!response.ok) {
|
|
200
255
|
const text = await response.text().catch(() => '');
|
|
201
|
-
throw new Error(`[P2PClient]
|
|
256
|
+
throw new Error(`[P2PClient] Fix trigger failed (${response.status}): ${text}`);
|
|
202
257
|
}
|
|
203
258
|
return response.json();
|
|
204
259
|
}
|
package/dist/YaverFeedback.d.ts
CHANGED
|
@@ -116,10 +116,6 @@ export declare class YaverFeedback {
|
|
|
116
116
|
* Available after init if agentUrl is set, or after first successful discovery.
|
|
117
117
|
*/
|
|
118
118
|
static getP2PClient(): P2PClient | null;
|
|
119
|
-
/** Returns the current feedback mode. */
|
|
120
|
-
static getFeedbackMode(): 'live' | 'narrated' | 'batch';
|
|
121
|
-
/** Returns the agent commentary level (0-10). */
|
|
122
|
-
static getCommentaryLevel(): number;
|
|
123
119
|
/**
|
|
124
120
|
* Record a business event. Routes through BlackBox so the agent
|
|
125
121
|
* persists it to the analytics ledger (no dashboards — export
|
package/dist/YaverFeedback.js
CHANGED
|
@@ -65,8 +65,6 @@ class YaverFeedback {
|
|
|
65
65
|
config = {
|
|
66
66
|
trigger: 'shake',
|
|
67
67
|
maxRecordingDuration: 120,
|
|
68
|
-
feedbackMode: 'batch',
|
|
69
|
-
agentCommentaryLevel: 0,
|
|
70
68
|
autoLogin: true,
|
|
71
69
|
...cfg,
|
|
72
70
|
};
|
|
@@ -464,14 +462,6 @@ class YaverFeedback {
|
|
|
464
462
|
static getP2PClient() {
|
|
465
463
|
return p2pClient;
|
|
466
464
|
}
|
|
467
|
-
/** Returns the current feedback mode. */
|
|
468
|
-
static getFeedbackMode() {
|
|
469
|
-
return config?.feedbackMode ?? 'batch';
|
|
470
|
-
}
|
|
471
|
-
/** Returns the agent commentary level (0-10). */
|
|
472
|
-
static getCommentaryLevel() {
|
|
473
|
-
return config?.agentCommentaryLevel ?? 0;
|
|
474
|
-
}
|
|
475
465
|
// ─── One-stop SaaS replacement methods ─────────────────────────
|
|
476
466
|
//
|
|
477
467
|
// These are the three solo-dev SaaS-replacement entry points
|
|
@@ -36,22 +36,18 @@ describe('YaverFeedback', () => {
|
|
|
36
36
|
expect(cfg.agentUrl).toBe('http://localhost:18080');
|
|
37
37
|
expect(cfg.trigger).toBe('shake');
|
|
38
38
|
expect(cfg.maxRecordingDuration).toBe(120);
|
|
39
|
-
expect(cfg.feedbackMode).toBe('batch');
|
|
40
|
-
expect(cfg.agentCommentaryLevel).toBe(0);
|
|
41
39
|
});
|
|
42
40
|
it('respects user-provided values over defaults', () => {
|
|
43
41
|
YaverFeedback_1.YaverFeedback.init({
|
|
44
42
|
authToken: 'tok',
|
|
45
43
|
trigger: 'floating-button',
|
|
46
44
|
maxRecordingDuration: 60,
|
|
47
|
-
|
|
48
|
-
agentCommentaryLevel: 7,
|
|
45
|
+
strictNativeAuth: true,
|
|
49
46
|
});
|
|
50
47
|
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
51
48
|
expect(cfg.trigger).toBe('floating-button');
|
|
52
49
|
expect(cfg.maxRecordingDuration).toBe(60);
|
|
53
|
-
expect(cfg.
|
|
54
|
-
expect(cfg.agentCommentaryLevel).toBe(7);
|
|
50
|
+
expect(cfg.strictNativeAuth).toBe(true);
|
|
55
51
|
});
|
|
56
52
|
it('with enabled=false sets enabled to false', () => {
|
|
57
53
|
YaverFeedback_1.YaverFeedback.init({
|
|
@@ -110,35 +106,6 @@ describe('YaverFeedback', () => {
|
|
|
110
106
|
expect(cfg.agentUrl).toBe('http://10.0.0.1:18080');
|
|
111
107
|
});
|
|
112
108
|
});
|
|
113
|
-
describe('getFeedbackMode()', () => {
|
|
114
|
-
it('defaults to batch when no config', () => {
|
|
115
|
-
// After any init, feedbackMode defaults to 'batch'
|
|
116
|
-
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok' });
|
|
117
|
-
expect(YaverFeedback_1.YaverFeedback.getFeedbackMode()).toBe('batch');
|
|
118
|
-
});
|
|
119
|
-
it('returns configured mode', () => {
|
|
120
|
-
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok', feedbackMode: 'narrated' });
|
|
121
|
-
expect(YaverFeedback_1.YaverFeedback.getFeedbackMode()).toBe('narrated');
|
|
122
|
-
});
|
|
123
|
-
it('returns live when configured', () => {
|
|
124
|
-
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok', feedbackMode: 'live' });
|
|
125
|
-
expect(YaverFeedback_1.YaverFeedback.getFeedbackMode()).toBe('live');
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
describe('getCommentaryLevel()', () => {
|
|
129
|
-
it('defaults to 0', () => {
|
|
130
|
-
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok' });
|
|
131
|
-
expect(YaverFeedback_1.YaverFeedback.getCommentaryLevel()).toBe(0);
|
|
132
|
-
});
|
|
133
|
-
it('returns configured level', () => {
|
|
134
|
-
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok', agentCommentaryLevel: 5 });
|
|
135
|
-
expect(YaverFeedback_1.YaverFeedback.getCommentaryLevel()).toBe(5);
|
|
136
|
-
});
|
|
137
|
-
it('returns max level when set to 10', () => {
|
|
138
|
-
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok', agentCommentaryLevel: 10 });
|
|
139
|
-
expect(YaverFeedback_1.YaverFeedback.getCommentaryLevel()).toBe(10);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
109
|
describe('startReport()', () => {
|
|
143
110
|
it('does nothing when not enabled', async () => {
|
|
144
111
|
YaverFeedback_1.YaverFeedback.init({ authToken: 'tok', enabled: false });
|
|
@@ -11,8 +11,6 @@ describe('React Native SDK types', () => {
|
|
|
11
11
|
expect(config.trigger).toBeUndefined();
|
|
12
12
|
expect(config.enabled).toBeUndefined();
|
|
13
13
|
expect(config.maxRecordingDuration).toBeUndefined();
|
|
14
|
-
expect(config.feedbackMode).toBeUndefined();
|
|
15
|
-
expect(config.agentCommentaryLevel).toBeUndefined();
|
|
16
14
|
});
|
|
17
15
|
it('can be constructed with all optional fields', () => {
|
|
18
16
|
const config = {
|
|
@@ -21,12 +19,10 @@ describe('React Native SDK types', () => {
|
|
|
21
19
|
trigger: 'shake',
|
|
22
20
|
enabled: true,
|
|
23
21
|
maxRecordingDuration: 60,
|
|
24
|
-
|
|
25
|
-
agentCommentaryLevel: 7,
|
|
22
|
+
strictNativeAuth: true,
|
|
26
23
|
};
|
|
27
24
|
expect(config.trigger).toBe('shake');
|
|
28
|
-
expect(config.
|
|
29
|
-
expect(config.agentCommentaryLevel).toBe(7);
|
|
25
|
+
expect(config.strictNativeAuth).toBe(true);
|
|
30
26
|
});
|
|
31
27
|
it('accepts all trigger types', () => {
|
|
32
28
|
const triggers = ['shake', 'floating-button', 'manual'];
|
|
@@ -35,13 +31,6 @@ describe('React Native SDK types', () => {
|
|
|
35
31
|
expect(config.trigger).toBe(trigger);
|
|
36
32
|
});
|
|
37
33
|
});
|
|
38
|
-
it('accepts all feedback modes', () => {
|
|
39
|
-
const modes = ['live', 'narrated', 'batch'];
|
|
40
|
-
modes.forEach((mode) => {
|
|
41
|
-
const config = { authToken: 'tok', feedbackMode: mode };
|
|
42
|
-
expect(config.feedbackMode).toBe(mode);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
34
|
});
|
|
46
35
|
describe('FeedbackBundle', () => {
|
|
47
36
|
it('can be constructed with required fields', () => {
|
|
@@ -67,9 +56,8 @@ describe('React Native SDK types', () => {
|
|
|
67
56
|
expect(bundle.metadata.device.platform).toBe('ios');
|
|
68
57
|
expect(bundle.screenshots).toEqual([]);
|
|
69
58
|
expect(bundle.video).toBeUndefined();
|
|
70
|
-
expect(bundle.audio).toBeUndefined();
|
|
71
59
|
});
|
|
72
|
-
it('can include optional video
|
|
60
|
+
it('can include optional video + screenshots', () => {
|
|
73
61
|
const bundle = {
|
|
74
62
|
metadata: {
|
|
75
63
|
timestamp: '2026-03-24T12:00:00Z',
|
|
@@ -84,11 +72,9 @@ describe('React Native SDK types', () => {
|
|
|
84
72
|
userNote: 'This button does not work',
|
|
85
73
|
},
|
|
86
74
|
video: '/tmp/recording.mp4',
|
|
87
|
-
audio: '/tmp/voice.m4a',
|
|
88
75
|
screenshots: ['/tmp/ss1.png', '/tmp/ss2.png'],
|
|
89
76
|
};
|
|
90
77
|
expect(bundle.video).toBe('/tmp/recording.mp4');
|
|
91
|
-
expect(bundle.audio).toBe('/tmp/voice.m4a');
|
|
92
78
|
expect(bundle.screenshots).toHaveLength(2);
|
|
93
79
|
expect(bundle.metadata.userNote).toBe('This button does not work');
|
|
94
80
|
});
|
|
@@ -187,24 +173,6 @@ describe('React Native SDK types', () => {
|
|
|
187
173
|
});
|
|
188
174
|
});
|
|
189
175
|
});
|
|
190
|
-
describe('AgentCommentary', () => {
|
|
191
|
-
it('has correct structure', () => {
|
|
192
|
-
const commentary = {
|
|
193
|
-
id: 'cmt-1',
|
|
194
|
-
timestamp: '2026-03-24T12:00:00Z',
|
|
195
|
-
message: 'I see a layout issue on the login screen',
|
|
196
|
-
type: 'observation',
|
|
197
|
-
};
|
|
198
|
-
expect(commentary.type).toBe('observation');
|
|
199
|
-
});
|
|
200
|
-
it('accepts all commentary types', () => {
|
|
201
|
-
const types = ['observation', 'suggestion', 'question', 'action'];
|
|
202
|
-
types.forEach((type) => {
|
|
203
|
-
const c = { id: '1', timestamp: 'now', message: 'test', type };
|
|
204
|
-
expect(c.type).toBe(type);
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
176
|
describe('FeedbackStreamEvent', () => {
|
|
209
177
|
it('has correct structure', () => {
|
|
210
178
|
const event = {
|
package/dist/capture.d.ts
CHANGED
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Screen capture
|
|
2
|
+
* Screen capture helpers — screenshot + video recording.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
15
|
* Capture the current screen as a PNG image.
|
|
11
16
|
* Requires `react-native-view-shot` to be installed.
|
|
12
|
-
*
|
|
17
|
+
*
|
|
18
|
+
* Note: the feedback modal should hide itself *before* calling this so the
|
|
19
|
+
* screenshot contains the underlying app state (the actual bug), not the
|
|
20
|
+
* modal. See `FeedbackModal.handleScreenshotForFix`.
|
|
13
21
|
*/
|
|
14
22
|
export declare function captureScreenshot(): Promise<string>;
|
|
15
23
|
/**
|
|
16
|
-
* Start recording
|
|
17
|
-
*
|
|
24
|
+
* Start a screen-recording session. Requires
|
|
25
|
+
* `react-native-record-screen` as a peer dep.
|
|
26
|
+
*
|
|
27
|
+
* The user must grant the iOS ReplayKit / Android MediaProjection
|
|
28
|
+
* permission the first time; the prompt is shown by the native module,
|
|
29
|
+
* not the SDK.
|
|
18
30
|
*/
|
|
19
|
-
export declare function
|
|
31
|
+
export declare function startVideoRecording(): Promise<void>;
|
|
20
32
|
/**
|
|
21
|
-
* Stop the current
|
|
22
|
-
* @returns Object with the file path and duration in seconds.
|
|
33
|
+
* Stop the current video recording and return the on-device file path.
|
|
23
34
|
*/
|
|
24
|
-
export declare function
|
|
35
|
+
export declare function stopVideoRecording(): Promise<{
|
|
25
36
|
path: string;
|
|
26
37
|
duration: number;
|
|
27
38
|
}>;
|
|
39
|
+
/** Whether a video recording is currently active. */
|
|
40
|
+
export declare function isVideoRecording(): boolean;
|
package/dist/capture.js
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Screen capture
|
|
3
|
+
* Screen capture helpers — screenshot + video recording.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Peer deps (all optional — loaded lazily):
|
|
6
|
+
* - `react-native-view-shot` — screenshot
|
|
7
|
+
* - `react-native-record-screen` — video recording (iOS ReplayKit /
|
|
8
|
+
* Android MediaProjection)
|
|
9
|
+
*
|
|
10
|
+
* Each helper surfaces a clear error if the module is missing so a host
|
|
11
|
+
* app knows exactly which peer dep to add. Audio-note / voice-command
|
|
12
|
+
* recording was removed in 0.7.0 — see FeedbackModal for the new
|
|
13
|
+
* 5-button surface.
|
|
9
14
|
*/
|
|
10
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
16
|
exports.captureScreenshot = captureScreenshot;
|
|
12
|
-
exports.
|
|
13
|
-
exports.
|
|
14
|
-
|
|
17
|
+
exports.startVideoRecording = startVideoRecording;
|
|
18
|
+
exports.stopVideoRecording = stopVideoRecording;
|
|
19
|
+
exports.isVideoRecording = isVideoRecording;
|
|
15
20
|
/**
|
|
16
21
|
* Capture the current screen as a PNG image.
|
|
17
22
|
* Requires `react-native-view-shot` to be installed.
|
|
18
|
-
*
|
|
23
|
+
*
|
|
24
|
+
* Note: the feedback modal should hide itself *before* calling this so the
|
|
25
|
+
* screenshot contains the underlying app state (the actual bug), not the
|
|
26
|
+
* modal. See `FeedbackModal.handleScreenshotForFix`.
|
|
19
27
|
*/
|
|
20
28
|
async function captureScreenshot() {
|
|
21
29
|
try {
|
|
@@ -27,48 +35,79 @@ async function captureScreenshot() {
|
|
|
27
35
|
return uri;
|
|
28
36
|
}
|
|
29
37
|
catch (err) {
|
|
30
|
-
throw new Error('[YaverFeedback] Screenshot capture failed.
|
|
38
|
+
throw new Error('[YaverFeedback] Screenshot capture failed. Install react-native-view-shot as a peer dep. ' +
|
|
31
39
|
String(err));
|
|
32
40
|
}
|
|
33
41
|
}
|
|
42
|
+
let videoRecorderModule = null;
|
|
43
|
+
let videoRecordingActive = false;
|
|
34
44
|
/**
|
|
35
|
-
* Start recording
|
|
36
|
-
*
|
|
45
|
+
* Start a screen-recording session. Requires
|
|
46
|
+
* `react-native-record-screen` as a peer dep.
|
|
47
|
+
*
|
|
48
|
+
* The user must grant the iOS ReplayKit / Android MediaProjection
|
|
49
|
+
* permission the first time; the prompt is shown by the native module,
|
|
50
|
+
* not the SDK.
|
|
37
51
|
*/
|
|
38
|
-
async function
|
|
52
|
+
async function startVideoRecording() {
|
|
53
|
+
if (videoRecordingActive) {
|
|
54
|
+
throw new Error('[YaverFeedback] A video recording is already in progress.');
|
|
55
|
+
}
|
|
39
56
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
videoRecorderModule = require('react-native-record-screen').default ??
|
|
58
|
+
require('react-native-record-screen');
|
|
59
|
+
if (typeof videoRecorderModule.startRecording !== 'function') {
|
|
60
|
+
throw new Error('react-native-record-screen missing startRecording()');
|
|
61
|
+
}
|
|
62
|
+
const result = await videoRecorderModule.startRecording({
|
|
63
|
+
mic: false,
|
|
64
|
+
width: 720,
|
|
65
|
+
bitrate: 1024 * 1000,
|
|
66
|
+
});
|
|
67
|
+
if (result && result.status && result.status !== 'success') {
|
|
68
|
+
throw new Error(`startRecording returned ${result.status}`);
|
|
69
|
+
}
|
|
70
|
+
videoRecordingActive = true;
|
|
43
71
|
}
|
|
44
72
|
catch (err) {
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
videoRecorderModule = null;
|
|
74
|
+
videoRecordingActive = false;
|
|
75
|
+
throw new Error('[YaverFeedback] Could not start screen recording. Install react-native-record-screen. ' +
|
|
47
76
|
String(err));
|
|
48
77
|
}
|
|
49
78
|
}
|
|
50
79
|
/**
|
|
51
|
-
* Stop the current
|
|
52
|
-
* @returns Object with the file path and duration in seconds.
|
|
80
|
+
* Stop the current video recording and return the on-device file path.
|
|
53
81
|
*/
|
|
54
|
-
async function
|
|
55
|
-
if (!
|
|
56
|
-
throw new Error('[YaverFeedback] No
|
|
82
|
+
async function stopVideoRecording() {
|
|
83
|
+
if (!videoRecordingActive || !videoRecorderModule) {
|
|
84
|
+
throw new Error('[YaverFeedback] No video recording in progress.');
|
|
57
85
|
}
|
|
58
86
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
87
|
+
const res = await videoRecorderModule.stopRecording();
|
|
88
|
+
videoRecordingActive = false;
|
|
89
|
+
const path = typeof res === 'string'
|
|
90
|
+
? res
|
|
91
|
+
: res?.result?.outputURL ??
|
|
92
|
+
res?.outputURL ??
|
|
93
|
+
res?.uri ??
|
|
94
|
+
'';
|
|
95
|
+
const durationMs = typeof res?.result?.duration === 'number'
|
|
96
|
+
? res.result.duration
|
|
97
|
+
: typeof res?.duration === 'number'
|
|
98
|
+
? res.duration
|
|
99
|
+
: 0;
|
|
100
|
+
if (!path) {
|
|
101
|
+
throw new Error('stopRecording() returned no file path');
|
|
102
|
+
}
|
|
68
103
|
return { path, duration: durationMs / 1000 };
|
|
69
104
|
}
|
|
70
105
|
catch (err) {
|
|
71
|
-
|
|
72
|
-
throw new Error('[YaverFeedback] Failed to stop
|
|
106
|
+
videoRecordingActive = false;
|
|
107
|
+
throw new Error('[YaverFeedback] Failed to stop screen recording. ' + String(err));
|
|
73
108
|
}
|
|
74
109
|
}
|
|
110
|
+
/** Whether a video recording is currently active. */
|
|
111
|
+
function isVideoRecording() {
|
|
112
|
+
return videoRecordingActive;
|
|
113
|
+
}
|
package/dist/expo.d.ts
CHANGED
package/dist/expo.js
CHANGED
|
@@ -32,7 +32,6 @@ const YaverFeedback_1 = require("./YaverFeedback");
|
|
|
32
32
|
*
|
|
33
33
|
* Defaults:
|
|
34
34
|
* - trigger: 'shake'
|
|
35
|
-
* - feedbackMode: 'batch'
|
|
36
35
|
* - enabled: __DEV__ (only active in development)
|
|
37
36
|
*
|
|
38
37
|
* @param overrides - Optional partial config to override defaults
|
|
@@ -54,7 +53,6 @@ function initExpo(overrides) {
|
|
|
54
53
|
YaverFeedback_1.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/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* yaver-feedback-react-native — Visual feedback SDK for Yaver.
|
|
3
3
|
*
|
|
4
|
-
* Shake-to-report
|
|
5
|
-
*
|
|
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,
|
|
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
|
-
*
|
|
20
|
+
* strictNativeAuth: true,
|
|
16
21
|
* });
|
|
17
22
|
*
|
|
18
|
-
*
|
|
19
|
-
* <FeedbackProvider>
|
|
23
|
+
* <>
|
|
20
24
|
* <App />
|
|
21
|
-
*
|
|
25
|
+
* <FeedbackModal />
|
|
26
|
+
* </>
|
|
22
27
|
* ```
|
|
23
28
|
*/
|
|
24
29
|
export { YaverFeedback } from './YaverFeedback';
|
|
@@ -40,9 +45,9 @@ export { FeedbackModal } from './FeedbackModal';
|
|
|
40
45
|
export { FixReport } from './FixReport';
|
|
41
46
|
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';
|
|
42
47
|
export type { OAuthProvider, User, RemoteDevice, DeviceList, } from './auth';
|
|
43
|
-
export { captureScreenshot,
|
|
48
|
+
export { captureScreenshot, startVideoRecording, stopVideoRecording, isVideoRecording, } from './capture';
|
|
44
49
|
export { uploadFeedback } from './upload';
|
|
45
|
-
export type { FeedbackConfig, FeedbackBundle, FeedbackMetadata, DeviceInfo, AppInfo, TimelineEvent, FeedbackReport,
|
|
50
|
+
export type { FeedbackConfig, FeedbackBundle, FeedbackMetadata, DeviceInfo, AppInfo, TimelineEvent, FeedbackReport, FeedbackStreamEvent, VoiceCapability, CapturedError, TestFix, TestSession, } from './types';
|
|
46
51
|
export type { BlackBoxEvent, BlackBoxConfig, BlackBoxCommand, CommandHandler } from './BlackBox';
|
|
47
52
|
export type { DiscoveryResult } from './Discovery';
|
|
48
53
|
export type { FeedbackEvent } from './P2PClient';
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* yaver-feedback-react-native — Visual feedback SDK for Yaver.
|
|
4
4
|
*
|
|
5
|
-
* Shake-to-report
|
|
6
|
-
*
|
|
5
|
+
* Shake-to-report surface with five one-tap actions:
|
|
6
|
+
* 1. Hot Reload — instant JS reload
|
|
7
|
+
* 2. Screenshot & Fix — capture the screen under the modal and
|
|
8
|
+
* kick a fix task on the agent
|
|
9
|
+
* 3. Vibing — open a vibing session on the agent
|
|
10
|
+
* 4. Start / Stop Recording — screen recording toggle
|
|
11
|
+
* 5. Send Video — submit the last recording
|
|
7
12
|
*
|
|
8
13
|
* @example
|
|
9
14
|
* ```tsx
|
|
10
|
-
* import { YaverFeedback,
|
|
15
|
+
* import { YaverFeedback, FeedbackModal } from 'yaver-feedback-react-native';
|
|
11
16
|
*
|
|
12
17
|
* YaverFeedback.init({
|
|
13
18
|
* agentUrl: 'http://192.168.1.10:18080',
|
|
14
19
|
* authToken: 'your-token',
|
|
15
20
|
* trigger: 'shake',
|
|
16
|
-
*
|
|
21
|
+
* strictNativeAuth: true,
|
|
17
22
|
* });
|
|
18
23
|
*
|
|
19
|
-
*
|
|
20
|
-
* <FeedbackProvider>
|
|
24
|
+
* <>
|
|
21
25
|
* <App />
|
|
22
|
-
*
|
|
26
|
+
* <FeedbackModal />
|
|
27
|
+
* </>
|
|
23
28
|
* ```
|
|
24
29
|
*/
|
|
25
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.uploadFeedback = exports.
|
|
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.FixReport = exports.FeedbackModal = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.YaverFeedback = void 0;
|
|
27
32
|
var YaverFeedback_1 = require("./YaverFeedback");
|
|
28
33
|
Object.defineProperty(exports, "YaverFeedback", { enumerable: true, get: function () { return YaverFeedback_1.YaverFeedback; } });
|
|
29
34
|
var BlackBox_1 = require("./BlackBox");
|
|
@@ -75,7 +80,8 @@ Object.defineProperty(exports, "DEFAULT_WEB_BASE_URL", { enumerable: true, get:
|
|
|
75
80
|
Object.defineProperty(exports, "DEFAULT_OAUTH_REDIRECT", { enumerable: true, get: function () { return auth_1.DEFAULT_OAUTH_REDIRECT; } });
|
|
76
81
|
var capture_1 = require("./capture");
|
|
77
82
|
Object.defineProperty(exports, "captureScreenshot", { enumerable: true, get: function () { return capture_1.captureScreenshot; } });
|
|
78
|
-
Object.defineProperty(exports, "
|
|
79
|
-
Object.defineProperty(exports, "
|
|
83
|
+
Object.defineProperty(exports, "startVideoRecording", { enumerable: true, get: function () { return capture_1.startVideoRecording; } });
|
|
84
|
+
Object.defineProperty(exports, "stopVideoRecording", { enumerable: true, get: function () { return capture_1.stopVideoRecording; } });
|
|
85
|
+
Object.defineProperty(exports, "isVideoRecording", { enumerable: true, get: function () { return capture_1.isVideoRecording; } });
|
|
80
86
|
var upload_1 = require("./upload");
|
|
81
87
|
Object.defineProperty(exports, "uploadFeedback", { enumerable: true, get: function () { return upload_1.uploadFeedback; } });
|