yaver-feedback-react-native 0.9.9 → 0.9.10
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/app.plugin.js +24 -10
- package/dist/DogfoodRuntime.d.ts +6 -1
- package/dist/DogfoodRuntime.js +9 -3
- package/dist/DogfoodSessionUi.d.ts +1 -0
- package/dist/DogfoodSessionUi.js +5 -2
- package/dist/FeedbackModal.js +249 -278
- package/dist/FeedbackModalTheme.d.ts +5 -0
- package/dist/FeedbackModalTheme.js +29 -0
- package/dist/P2PClient.d.ts +2 -0
- package/dist/P2PClient.js +1 -1
- package/dist/P2PDogfoodDriver.js +17 -0
- package/dist/__tests__/DogfoodRuntime.test.js +14 -0
- package/dist/__tests__/FeedbackModalContract.test.d.ts +1 -0
- package/dist/__tests__/FeedbackModalContract.test.js +70 -0
- package/dist/__tests__/NativeDogfoodShortcut.test.js +23 -0
- package/dist/__tests__/P2PDogfoodDriver.test.js +4 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +4 -5
- package/ios/YaverHotReload.swift +3 -4
- package/package.json +1 -1
- package/src/DogfoodRuntime.ts +21 -4
- package/src/DogfoodSessionUi.tsx +6 -2
- package/src/FeedbackModal.tsx +319 -372
- package/src/FeedbackModalTheme.ts +29 -0
- package/src/P2PClient.ts +3 -1
- package/src/P2PDogfoodDriver.ts +17 -0
- package/src/__tests__/DogfoodRuntime.test.ts +15 -0
- package/src/__tests__/FeedbackModalContract.test.ts +80 -0
- package/src/__tests__/NativeDogfoodShortcut.test.ts +25 -0
- package/src/__tests__/P2PDogfoodDriver.test.ts +4 -0
- package/src/index.ts +4 -5
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DogfoodUiColors } from './DogfoodSessionUi';
|
|
2
|
+
|
|
3
|
+
/** High-contrast colors for shared Dogfood controls inside the light SDK sheet. */
|
|
4
|
+
export const FEEDBACK_DOGFOOD_LIGHT_COLORS: DogfoodUiColors = {
|
|
5
|
+
background: '#ffffff',
|
|
6
|
+
border: '#c9c9d5',
|
|
7
|
+
text: '#222229',
|
|
8
|
+
muted: '#5f5f6b',
|
|
9
|
+
accent: '#5645d8',
|
|
10
|
+
accentSoft: '#e7e5ff',
|
|
11
|
+
ready: '#137a3f',
|
|
12
|
+
attention: '#9a5700',
|
|
13
|
+
blocked: '#b42318',
|
|
14
|
+
console: '#ffffff',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** The log surface stays dark, but its foregrounds are explicit and readable. */
|
|
18
|
+
export const FEEDBACK_DOGFOOD_CONSOLE_COLORS: DogfoodUiColors = {
|
|
19
|
+
...FEEDBACK_DOGFOOD_LIGHT_COLORS,
|
|
20
|
+
background: '#15151b',
|
|
21
|
+
border: '#454552',
|
|
22
|
+
text: '#f8fafc',
|
|
23
|
+
muted: '#cbd5e1',
|
|
24
|
+
accent: '#a5b4fc',
|
|
25
|
+
ready: '#4ade80',
|
|
26
|
+
attention: '#fbbf24',
|
|
27
|
+
blocked: '#fca5a5',
|
|
28
|
+
console: '#15151b',
|
|
29
|
+
};
|
package/src/P2PClient.ts
CHANGED
|
@@ -315,7 +315,7 @@ function friendlyReloadError(status: number, body: string): string {
|
|
|
315
315
|
if (hasRefused && hasLoopback) {
|
|
316
316
|
return (
|
|
317
317
|
'No dev server running on your machine. ' +
|
|
318
|
-
'Start Metro with `yaver dev start
|
|
318
|
+
'Start Metro with `yaver dev start`, or describe the issue in Chat so the agent can inspect it.'
|
|
319
319
|
);
|
|
320
320
|
}
|
|
321
321
|
if (
|
|
@@ -491,6 +491,8 @@ export class P2PClient {
|
|
|
491
491
|
framework?: string;
|
|
492
492
|
frameworks?: string[];
|
|
493
493
|
surfaces?: string[];
|
|
494
|
+
branch?: string;
|
|
495
|
+
gitRemote?: string;
|
|
494
496
|
}>> {
|
|
495
497
|
const resp = await fetch(`${this.baseUrl}/projects`, { headers: this.authHeaders() });
|
|
496
498
|
if (!resp.ok) {
|
package/src/P2PDogfoodDriver.ts
CHANGED
|
@@ -56,8 +56,18 @@ export function createP2PDogfoodDriver(
|
|
|
56
56
|
retryable: true,
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
context.log({
|
|
60
|
+
text: `[runtime] source · ${target.label}${target.platform ? ` · ${target.platform}` : ''}`,
|
|
61
|
+
at: Date.now(),
|
|
62
|
+
stream: 'system',
|
|
63
|
+
});
|
|
59
64
|
context.setPhase('starting', `Starting ${target.label} over WebRTC…`);
|
|
60
65
|
const session = await client.startDogfoodRemoteRuntime(project.workDir, project.framework, target.id);
|
|
66
|
+
context.log({
|
|
67
|
+
text: `[runtime] ${session.status || 'starting'}${session.note ? ` · ${session.note}` : ''}`,
|
|
68
|
+
at: Date.now(),
|
|
69
|
+
stream: 'system',
|
|
70
|
+
});
|
|
61
71
|
context.registerCleanup(() => client.stopDogfoodRemoteRuntime(session.id), 'session');
|
|
62
72
|
return {
|
|
63
73
|
lane: 'webrtc',
|
|
@@ -67,6 +77,13 @@ export function createP2PDogfoodDriver(
|
|
|
67
77
|
}
|
|
68
78
|
context.setPhase(project.lane === 'hermes' ? 'compiling' : 'starting',
|
|
69
79
|
project.lane === 'hermes' ? `Compiling ${project.name} with Hermes…` : `Starting ${project.name} in the browser…`);
|
|
80
|
+
context.log({
|
|
81
|
+
text: project.lane === 'hermes'
|
|
82
|
+
? `[runtime] source · Hermes build on ${project.workDir}`
|
|
83
|
+
: `[runtime] source · browser build on ${project.workDir}`,
|
|
84
|
+
at: Date.now(),
|
|
85
|
+
stream: 'system',
|
|
86
|
+
});
|
|
70
87
|
const status = await client.startDogfoodDevServer({
|
|
71
88
|
framework: project.framework,
|
|
72
89
|
workDir: project.workDir,
|
|
@@ -124,6 +124,21 @@ describe('Dogfood lanes and console events', () => {
|
|
|
124
124
|
expect(swift.map((option) => [option.lane, option.supported])).toEqual([
|
|
125
125
|
['browser', false], ['hermes', false], ['webrtc', true],
|
|
126
126
|
]);
|
|
127
|
+
expect(dogfoodLaneOptions('kotlin', { nativeRuntimeAvailable: true })
|
|
128
|
+
.map((option) => [option.lane, option.supported])).toEqual([
|
|
129
|
+
['browser', false], ['hermes', false], ['webrtc', true],
|
|
130
|
+
]);
|
|
131
|
+
expect(defaultDogfoodLane('swift', { nativeRuntimeAvailable: true })).toBe('webrtc');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('a detected browser target adds real framework exceptions such as SwiftWasm', () => {
|
|
135
|
+
const swiftWasm = dogfoodLaneOptions('swift', {
|
|
136
|
+
nativeRuntimeAvailable: false,
|
|
137
|
+
browserRuntimeAvailable: true,
|
|
138
|
+
});
|
|
139
|
+
expect(swiftWasm.map((option) => [option.lane, option.supported])).toEqual([
|
|
140
|
+
['browser', true], ['hermes', false], ['webrtc', false],
|
|
141
|
+
]);
|
|
127
142
|
});
|
|
128
143
|
|
|
129
144
|
test('reads raw and replayed package-manager logs from /dev/events', () => {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import {
|
|
4
|
+
FEEDBACK_DOGFOOD_CONSOLE_COLORS,
|
|
5
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS,
|
|
6
|
+
} from '../FeedbackModalTheme';
|
|
7
|
+
|
|
8
|
+
const SOURCE_PATH = join(__dirname, '..', 'FeedbackModal.tsx');
|
|
9
|
+
|
|
10
|
+
describe('FeedbackModal authenticated chat contract', () => {
|
|
11
|
+
const source = readFileSync(SOURCE_PATH, 'utf8');
|
|
12
|
+
|
|
13
|
+
it('themes every shared Dogfood surface for the light feedback sheet', () => {
|
|
14
|
+
expect(source).toMatch(/<DogfoodStatusRail[\s\S]*?colors=\{FEEDBACK_DOGFOOD_LIGHT_COLORS\}/);
|
|
15
|
+
expect(source).toMatch(/<DogfoodLanePicker[\s\S]*?colors=\{FEEDBACK_DOGFOOD_LIGHT_COLORS\}/);
|
|
16
|
+
expect(source).toMatch(/<DogfoodLiveConsole[\s\S]*?colors=\{FEEDBACK_DOGFOOD_CONSOLE_COLORS\}/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('keeps normal light-sheet copy at WCAG AA contrast', () => {
|
|
20
|
+
const rgb = (hex: string) => hex.match(/[a-f\d]{2}/gi)!.map((part) => parseInt(part, 16) / 255);
|
|
21
|
+
const luminance = (hex: string) => {
|
|
22
|
+
const channels = rgb(hex).map((channel) => channel <= 0.03928
|
|
23
|
+
? channel / 12.92
|
|
24
|
+
: ((channel + 0.055) / 1.055) ** 2.4);
|
|
25
|
+
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
|
|
26
|
+
};
|
|
27
|
+
const ratio = (foreground: string, background: string) => {
|
|
28
|
+
const [lighter, darker] = [luminance(foreground), luminance(background)].sort((a, b) => b - a);
|
|
29
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
for (const foreground of [
|
|
33
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS.text,
|
|
34
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS.muted,
|
|
35
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS.accent,
|
|
36
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS.ready,
|
|
37
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS.attention,
|
|
38
|
+
FEEDBACK_DOGFOOD_LIGHT_COLORS.blocked,
|
|
39
|
+
]) {
|
|
40
|
+
expect(ratio(foreground, FEEDBACK_DOGFOOD_LIGHT_COLORS.background)).toBeGreaterThanOrEqual(4.5);
|
|
41
|
+
}
|
|
42
|
+
expect(ratio(FEEDBACK_DOGFOOD_CONSOLE_COLORS.text, FEEDBACK_DOGFOOD_CONSOLE_COLORS.console))
|
|
43
|
+
.toBeGreaterThanOrEqual(4.5);
|
|
44
|
+
expect(ratio(FEEDBACK_DOGFOOD_CONSOLE_COLORS.muted, FEEDBACK_DOGFOOD_CONSOLE_COLORS.console))
|
|
45
|
+
.toBeGreaterThanOrEqual(4.5);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('uses Chat as the authenticated entry surface without legacy command buttons', () => {
|
|
49
|
+
expect(source).toContain("setActiveTab(authenticated ? 'chat' : 'settings')");
|
|
50
|
+
expect(source).toContain('setShowVibeInput(authenticated || directDogfood)');
|
|
51
|
+
expect(source).not.toContain('Screenshot & Fix');
|
|
52
|
+
expect(source).not.toContain('<DeployPanel');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('keeps Dogfood setup to box, runner, and checkout before asking for a runtime', () => {
|
|
56
|
+
const setupSteps = source.match(/const dogfoodSetupSteps = \[([\s\S]*?)\n \];/)?.[1] || '';
|
|
57
|
+
expect([...setupSteps.matchAll(/key: '([^']+)'/g)].map((match) => match[1]))
|
|
58
|
+
.toEqual(['box', 'runner', 'checkout']);
|
|
59
|
+
expect(setupSteps).not.toContain("key: 'oauth'");
|
|
60
|
+
expect(setupSteps).not.toContain("key: 'installation'");
|
|
61
|
+
expect(setupSteps).not.toContain("key: 'model'");
|
|
62
|
+
expect(setupSteps).not.toContain("key: 'lane'");
|
|
63
|
+
expect(source).toContain("type DogfoodSetupStage = 'setup' | 'lane' | 'runtime'");
|
|
64
|
+
expect(source).toContain('label="Choose runtime"');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('passes the selected native target and labels the live log source', () => {
|
|
68
|
+
expect(source).toContain("nativeTargetId: dogfoodLane === 'webrtc' ? dogfoodNativeTargetId : undefined");
|
|
69
|
+
expect(source).toMatch(/<DogfoodLiveConsole[\s\S]*?sourceLabel=\{dogfoodSourceLabel\}/);
|
|
70
|
+
expect(source).toContain('Simulator, emulator, or device');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('has one iOS keyboard inset owner and no gesture-stealing sheet Pressable', () => {
|
|
74
|
+
expect(source).toContain('automaticallyAdjustKeyboardInsets={Platform.OS === \'ios\'}');
|
|
75
|
+
expect(source).not.toContain('<KeyboardAvoidingView');
|
|
76
|
+
expect(source).not.toContain('keyboardInset');
|
|
77
|
+
expect(source).toContain('<Pressable style={styles.backdrop} onPress={handleClose}');
|
|
78
|
+
expect(source).toMatch(/<View[\s\S]{0,300}?style=\{\[\s*styles\.modal/);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -3,12 +3,37 @@ import { join } from 'path';
|
|
|
3
3
|
|
|
4
4
|
const plugin = require('../../app.plugin.js') as {
|
|
5
5
|
__test: {
|
|
6
|
+
patchHotReloadBootConfirmation(contents: string): string;
|
|
6
7
|
patchDogfoodAppShortcut(contents: string): string;
|
|
7
8
|
patchDogfoodSceneDelegate(contents: string): string;
|
|
8
9
|
};
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
describe('native Dogfood shortcut contract', () => {
|
|
13
|
+
it('never treats process uptime as a successful React render', () => {
|
|
14
|
+
const staleHook = `
|
|
15
|
+
// Crash-revert safety net: clear the boot-attempt counter once
|
|
16
|
+
// RN renders its first frame, OR after 10 s of uptime — whichever
|
|
17
|
+
// fires first. If neither fires (bundle crashes before render),
|
|
18
|
+
// YaverHotReload.bundleURL() will eventually revert to the
|
|
19
|
+
// TestFlight-installed bundle after 3 failed boots. See
|
|
20
|
+
// YaverHotReload.swift for the full state machine.
|
|
21
|
+
NotificationCenter.default.addObserver(
|
|
22
|
+
forName: NSNotification.Name(rawValue: "RCTContentDidAppearNotification"),
|
|
23
|
+
object: nil,
|
|
24
|
+
queue: .main
|
|
25
|
+
) { _ in YaverHotReload.markBootSuccessful() }
|
|
26
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
|
|
27
|
+
YaverHotReload.markBootSuccessful()
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
const patched = plugin.__test.patchHotReloadBootConfirmation(staleHook);
|
|
31
|
+
expect(patched).toContain('RCTContentDidAppearNotification');
|
|
32
|
+
expect(patched).toContain('only a real first React frame confirms boot');
|
|
33
|
+
expect(patched).not.toContain('asyncAfter(deadline: .now() + 10)');
|
|
34
|
+
expect(plugin.__test.patchHotReloadBootConfirmation(patched)).toBe(patched);
|
|
35
|
+
});
|
|
36
|
+
|
|
12
37
|
it('patches cold and warm iOS shortcut delivery idempotently', () => {
|
|
13
38
|
const source = `
|
|
14
39
|
public class AppDelegate: ExpoAppDelegate {
|
|
@@ -50,12 +50,16 @@ describe('createP2PDogfoodDriver', () => {
|
|
|
50
50
|
})),
|
|
51
51
|
stopDogfoodRemoteRuntime: closeRuntime,
|
|
52
52
|
} as unknown as P2PClient;
|
|
53
|
+
const snapshots: string[][] = [];
|
|
53
54
|
const controller = new DogfoodController(
|
|
54
55
|
{ name: 'Native app', framework: 'flutter', workDir: '/workspace/app', lane: 'webrtc' },
|
|
55
56
|
createP2PDogfoodDriver(client),
|
|
57
|
+
{ onChange: (snapshot) => snapshots.push(snapshot.logs.map((line) => line.text)) },
|
|
56
58
|
);
|
|
57
59
|
|
|
58
60
|
await expect(controller.trigger()).resolves.toMatchObject({ lane: 'webrtc', sessionId: 'runtime-1' });
|
|
61
|
+
expect(snapshots.flat()).toContain('[runtime] source · iPhone simulator');
|
|
62
|
+
expect(snapshots.flat()).toContain('[runtime] starting');
|
|
59
63
|
expect(closeRuntime).not.toHaveBeenCalled();
|
|
60
64
|
await controller.stop();
|
|
61
65
|
expect(closeRuntime).toHaveBeenCalledWith('runtime-1');
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* yaver-feedback-react-native — Visual feedback SDK for Yaver.
|
|
3
3
|
*
|
|
4
|
-
* Shake-to-report surface with
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the fix loop
|
|
4
|
+
* Shake-to-report surface with one primary action: Chat. The connected agent
|
|
5
|
+
* receives the current-screen context and can use its MCP tools for fixes,
|
|
6
|
+
* reloads, and deploys without duplicating those commands as modal buttons.
|
|
7
|
+
* Machine, runner, model, and Dogfood configuration remain under Settings.
|
|
9
8
|
*
|
|
10
9
|
* The small quick-access icon stays hidden until the first shake by
|
|
11
10
|
* default on mobile, then remains available unless the user hides it.
|