yaver-feedback-react-native 0.9.7 → 0.9.9

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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const react_native_1 = require("react-native");
4
4
  const YaverFeedback_1 = require("../YaverFeedback");
5
+ const auth_1 = require("../auth");
5
6
  // Mock react-native: DeviceEventEmitter for event dispatch + Platform so
6
7
  // ShakeDetector.start() can branch on iOS without hitting a real RN runtime.
7
8
  jest.mock('react-native', () => ({
@@ -15,6 +16,10 @@ jest.mock('react-native', () => ({
15
16
  setDogfoodShortcut: jest.fn(async () => true),
16
17
  consumeDogfoodShortcut: jest.fn(async () => false),
17
18
  },
19
+ YaverDogfoodGesture: {
20
+ getCapability: jest.fn(async () => ({ supported: true, enabled: false, reason: 'supported', platform: 'ios' })),
21
+ setEnabled: jest.fn(async (next) => ({ supported: true, enabled: next, reason: 'supported', platform: 'ios' })),
22
+ },
18
23
  },
19
24
  AppState: { addEventListener: jest.fn(() => ({ remove: jest.fn() })) },
20
25
  }));
@@ -32,8 +37,10 @@ jest.mock('../auth', () => ({
32
37
  getDogfoodAccountAccess: jest.fn(async (_appId, token) => ({
33
38
  authenticated: token === 'owner-token',
34
39
  ownerAuthorized: token === 'owner-token',
40
+ accountAuthorized: token === 'owner-token',
35
41
  installationAuthorized: token === 'owner-token',
36
42
  })),
43
+ setDogfoodControlPreference: jest.fn(async () => true),
37
44
  saveSelectedDeviceId: jest.fn(async () => { }),
38
45
  clearToken: jest.fn(async () => { }),
39
46
  clearSelectedDeviceId: jest.fn(async () => { }),
@@ -62,6 +69,25 @@ beforeEach(() => {
62
69
  jest.restoreAllMocks();
63
70
  jest.clearAllMocks();
64
71
  });
72
+ function activeDogfood(controlGesture = true) {
73
+ return {
74
+ enabled: true,
75
+ appId: 'io.example.app',
76
+ installationId: 'phone-1',
77
+ installationStatus: 'active',
78
+ controlGesture,
79
+ };
80
+ }
81
+ function initActiveDogfood(options = {}) {
82
+ const controlGesture = options.controlGesture ?? true;
83
+ YaverFeedback_1.YaverFeedback.init({
84
+ authToken: options.authToken,
85
+ bundleId: 'io.example.app',
86
+ dogfood: { controlGesture },
87
+ });
88
+ Object.assign(YaverFeedback_1.YaverFeedback.getConfig().dogfood, activeDogfood(controlGesture));
89
+ jest.clearAllMocks();
90
+ }
65
91
  describe('YaverFeedback', () => {
66
92
  describe('Dogfood onboarding', () => {
67
93
  it('starts with Yaver OAuth when the host has no session', async () => {
@@ -96,6 +122,17 @@ describe('YaverFeedback', () => {
96
122
  unsubscribe();
97
123
  expect(states[states.length - 1]).toBe('auth-required');
98
124
  });
125
+ it('returns a visible structured error when access verification fails', async () => {
126
+ YaverFeedback_1.YaverFeedback.init({ enabled: true, authToken: 'owner-token' });
127
+ auth_1.getDogfoodAccountAccess.mockRejectedValueOnce(new Error('Access service unavailable'));
128
+ const state = await YaverFeedback_1.YaverFeedback.beginDogfoodOnboarding({ appId: 'io.example.app' });
129
+ expect(state).toEqual({
130
+ phase: 'error',
131
+ appId: 'io.example.app',
132
+ error: 'Access service unavailable',
133
+ });
134
+ expect(react_native_1.DeviceEventEmitter.emit).not.toHaveBeenCalledWith('yaverFeedback:startReport');
135
+ });
99
136
  it('lets a host ACL hide its affordance without opening auth UI', async () => {
100
137
  YaverFeedback_1.YaverFeedback.init({
101
138
  enabled: true,
@@ -113,6 +150,7 @@ describe('YaverFeedback', () => {
113
150
  appId: 'io.example.app',
114
151
  yaverAuthenticated: true,
115
152
  ownerAuthorized: false,
153
+ accountAuthorized: true,
116
154
  installationId: 'phone-1',
117
155
  deviceState: 'active',
118
156
  authorized: true,
@@ -128,6 +166,7 @@ describe('YaverFeedback', () => {
128
166
  appId: 'io.example.app',
129
167
  yaverAuthenticated: false,
130
168
  ownerAuthorized: false,
169
+ accountAuthorized: true,
131
170
  installationId: 'phone-1',
132
171
  deviceState: 'active',
133
172
  authorized: false,
@@ -145,6 +184,7 @@ describe('YaverFeedback', () => {
145
184
  appId: 'io.example.app',
146
185
  yaverAuthenticated: true,
147
186
  ownerAuthorized: false,
187
+ accountAuthorized: true,
148
188
  installationId: 'phone-1',
149
189
  deviceState: 'active',
150
190
  authorized: true,
@@ -153,6 +193,139 @@ describe('YaverFeedback', () => {
153
193
  expect(react_native_1.NativeModules.YaverHotReload.setDogfoodShortcut)
154
194
  .toHaveBeenCalledWith(false, 'Dogfood');
155
195
  });
196
+ it('keeps the Y visible until the authorized phone has completed onboarding', async () => {
197
+ initActiveDogfood();
198
+ jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
199
+ appId: 'io.example.app',
200
+ yaverAuthenticated: true,
201
+ ownerAuthorized: false,
202
+ accountAuthorized: true,
203
+ installationId: 'phone-1',
204
+ deviceState: 'active',
205
+ authorized: true,
206
+ });
207
+ const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
208
+ expect(state).toMatchObject({
209
+ onboardingSeen: false,
210
+ presentation: 'minimized-y',
211
+ gestureSupported: true,
212
+ gestureEnabled: false,
213
+ fallbackVisible: true,
214
+ });
215
+ expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(false, 900);
216
+ });
217
+ it('uses the invisible three-finger hold only after onboarding selects it', async () => {
218
+ initActiveDogfood();
219
+ jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
220
+ appId: 'io.example.app',
221
+ yaverAuthenticated: true,
222
+ ownerAuthorized: false,
223
+ accountAuthorized: true,
224
+ installationId: 'phone-1',
225
+ deviceState: 'active',
226
+ authorized: true,
227
+ controlPresentation: 'auto',
228
+ controlOnboardingSeen: true,
229
+ });
230
+ const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
231
+ expect(state).toMatchObject({
232
+ onboardingSeen: true,
233
+ presentation: 'auto',
234
+ gestureSupported: true,
235
+ gestureEnabled: true,
236
+ fallbackVisible: false,
237
+ });
238
+ expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(true, 900);
239
+ });
240
+ it('keeps the Y when native capability reports support but enabling the gesture fails', async () => {
241
+ initActiveDogfood();
242
+ jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
243
+ appId: 'io.example.app',
244
+ yaverAuthenticated: true,
245
+ ownerAuthorized: false,
246
+ accountAuthorized: true,
247
+ installationId: 'phone-1',
248
+ deviceState: 'active',
249
+ authorized: true,
250
+ controlPresentation: 'auto',
251
+ controlOnboardingSeen: true,
252
+ });
253
+ react_native_1.NativeModules.YaverDogfoodGesture.setEnabled.mockResolvedValueOnce({
254
+ supported: true,
255
+ enabled: false,
256
+ reason: 'supported',
257
+ platform: 'ios',
258
+ });
259
+ const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
260
+ expect(state).toMatchObject({
261
+ gestureSupported: true,
262
+ gestureEnabled: false,
263
+ fallbackVisible: true,
264
+ reason: 'gesture-enable-failed',
265
+ });
266
+ });
267
+ it('persists first-run completion for the exact account app installation', async () => {
268
+ initActiveDogfood({ authToken: 'owner-token' });
269
+ jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
270
+ appId: 'io.example.app',
271
+ yaverAuthenticated: true,
272
+ ownerAuthorized: true,
273
+ accountAuthorized: true,
274
+ installationId: 'phone-1',
275
+ deviceState: 'active',
276
+ authorized: true,
277
+ controlOnboardingSeen: false,
278
+ });
279
+ const state = await YaverFeedback_1.YaverFeedback.setDogfoodControlPresentation('minimized-y');
280
+ expect(state).toMatchObject({ onboardingSeen: true, presentation: 'minimized-y', fallbackVisible: true });
281
+ expect(auth_1.setDogfoodControlPreference).toHaveBeenCalledWith(expect.objectContaining({
282
+ appId: 'io.example.app',
283
+ installationId: 'phone-1',
284
+ token: 'owner-token',
285
+ presentation: 'minimized-y',
286
+ controlOnboardingSeen: true,
287
+ }));
288
+ });
289
+ it('falls back to the minimized Y when accessibility owns multi-touch', async () => {
290
+ initActiveDogfood({ controlGesture: { durationMs: 1200 } });
291
+ jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
292
+ appId: 'io.example.app',
293
+ yaverAuthenticated: true,
294
+ ownerAuthorized: false,
295
+ accountAuthorized: true,
296
+ installationId: 'phone-1',
297
+ deviceState: 'active',
298
+ authorized: true,
299
+ });
300
+ react_native_1.NativeModules.YaverDogfoodGesture.getCapability.mockResolvedValueOnce({
301
+ supported: false,
302
+ enabled: false,
303
+ reason: 'accessibility-touch-exploration',
304
+ platform: 'ios',
305
+ });
306
+ const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
307
+ expect(state).toMatchObject({ gestureSupported: false, gestureEnabled: false, fallbackVisible: true });
308
+ expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(false, 1200);
309
+ });
310
+ it('suppresses guest controls inside the Yaver split-view container', async () => {
311
+ initActiveDogfood();
312
+ react_native_1.NativeModules.YaverInfo = { isYaver: true };
313
+ const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
314
+ delete react_native_1.NativeModules.YaverInfo;
315
+ expect(state).toMatchObject({ gestureEnabled: false, fallbackVisible: false, reason: 'yaver-host-owns-controls' });
316
+ });
317
+ it('removes the gesture and fallback Y when Dogfood exits', async () => {
318
+ initActiveDogfood();
319
+ await YaverFeedback_1.YaverFeedback.exitDogfoodMode();
320
+ const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
321
+ expect(state).toMatchObject({
322
+ authorized: false,
323
+ gestureEnabled: false,
324
+ fallbackVisible: false,
325
+ reason: 'dogfood-session-inactive',
326
+ });
327
+ expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(false, 900);
328
+ });
156
329
  });
157
330
  describe('init()', () => {
158
331
  it('sets config correctly with defaults', () => {
package/dist/auth.d.ts CHANGED
@@ -52,8 +52,23 @@ export declare function clearSelectedDeviceId(): Promise<void>;
52
52
  export declare function getDogfoodAccountAccess(appId: string, token: string, installationId?: string): Promise<{
53
53
  authenticated: boolean;
54
54
  ownerAuthorized: boolean;
55
+ accountAuthorized: boolean;
55
56
  installationAuthorized: boolean;
57
+ controlPresentation?: 'auto' | 'minimized-y';
58
+ gestureSupported?: boolean;
59
+ gestureCapabilityReason?: string;
60
+ controlOnboardingSeen?: boolean;
56
61
  }>;
62
+ export declare function setDogfoodControlPreference(input: {
63
+ appId: string;
64
+ installationId: string;
65
+ token: string;
66
+ presentation: 'auto' | 'minimized-y';
67
+ gestureSupported: boolean;
68
+ gestureCapabilityReason: string;
69
+ gesturePlatform: string;
70
+ controlOnboardingSeen?: boolean;
71
+ }): Promise<boolean>;
57
72
  export declare function validateToken(token: string): Promise<User | null>;
58
73
  /**
59
74
  * Sign in with Apple using the native ASAuthorization flow. Requires
package/dist/auth.js CHANGED
@@ -32,6 +32,7 @@ exports.getSelectedDeviceId = getSelectedDeviceId;
32
32
  exports.saveSelectedDeviceId = saveSelectedDeviceId;
33
33
  exports.clearSelectedDeviceId = clearSelectedDeviceId;
34
34
  exports.getDogfoodAccountAccess = getDogfoodAccountAccess;
35
+ exports.setDogfoodControlPreference = setDogfoodControlPreference;
35
36
  exports.validateToken = validateToken;
36
37
  exports.signInWithApple = signInWithApple;
37
38
  exports.signInWithOAuth = signInWithOAuth;
@@ -202,16 +203,54 @@ async function getDogfoodAccountAccess(appId, token, installationId) {
202
203
  });
203
204
  clearTimeout(timeout);
204
205
  if (!response.ok)
205
- return { authenticated: false, ownerAuthorized: false, installationAuthorized: false };
206
+ return { authenticated: false, ownerAuthorized: false, accountAuthorized: false, installationAuthorized: false };
206
207
  const result = await response.json();
207
208
  return {
208
209
  authenticated: result?.authenticated === true,
209
210
  ownerAuthorized: result?.ownerAuthorized === true,
211
+ accountAuthorized: result?.accountAuthorized === true,
210
212
  installationAuthorized: result?.installationAuthorized === true,
213
+ controlPresentation: result?.controlPresentation === 'minimized-y' ? 'minimized-y'
214
+ : result?.controlPresentation === 'auto' ? 'auto' : undefined,
215
+ gestureSupported: typeof result?.gestureSupported === 'boolean' ? result.gestureSupported : undefined,
216
+ gestureCapabilityReason: typeof result?.gestureCapabilityReason === 'string'
217
+ ? result.gestureCapabilityReason
218
+ : undefined,
219
+ controlOnboardingSeen: result?.controlOnboardingSeen === true,
211
220
  };
212
221
  }
213
222
  catch {
214
- return { authenticated: false, ownerAuthorized: false, installationAuthorized: false };
223
+ return { authenticated: false, ownerAuthorized: false, accountAuthorized: false, installationAuthorized: false };
224
+ }
225
+ }
226
+ async function setDogfoodControlPreference(input) {
227
+ const controller = new AbortController();
228
+ const timeout = setTimeout(() => controller.abort(), 5000);
229
+ try {
230
+ const response = await fetch(`${convexSiteUrl}/dogfood/control-preference`, {
231
+ method: 'POST',
232
+ headers: {
233
+ Authorization: `Bearer ${input.token}`,
234
+ 'Content-Type': 'application/json',
235
+ },
236
+ body: JSON.stringify({
237
+ appId: input.appId,
238
+ installationId: input.installationId,
239
+ presentation: input.presentation,
240
+ gestureSupported: input.gestureSupported,
241
+ gestureCapabilityReason: input.gestureCapabilityReason,
242
+ gesturePlatform: input.gesturePlatform,
243
+ controlOnboardingSeen: input.controlOnboardingSeen === true,
244
+ }),
245
+ signal: controller.signal,
246
+ });
247
+ return response.ok;
248
+ }
249
+ catch {
250
+ return false;
251
+ }
252
+ finally {
253
+ clearTimeout(timeout);
215
254
  }
216
255
  }
217
256
  // ─── Token validation ──────────────────────────────────────────────────
@@ -4,10 +4,18 @@ export interface DogfoodAccessSnapshot {
4
4
  yaverAuthenticated: boolean;
5
5
  /** Backend-confirmed owner/maintainer of this exact appId. */
6
6
  ownerAuthorized: boolean;
7
+ /** Backend-confirmed app assignment for this Yaver account. App owners are
8
+ * assigned implicitly; other accounts require an active owner grant. */
9
+ accountAuthorized: boolean;
7
10
  installationId?: string;
8
11
  deviceState: 'unknown' | 'unregistered' | 'pending' | 'active' | 'cancelled' | 'revoked' | 'superseded';
9
- /** True only for a backend-approved device key or an authenticated owner. */
12
+ /** True only for an assigned account and this backend-approved device key. */
10
13
  authorized: boolean;
14
+ /** OAuth-backed preference scoped to this user + app + installation. */
15
+ controlPresentation?: 'auto' | 'minimized-y';
16
+ gestureSupported?: boolean;
17
+ gestureCapabilityReason?: string;
18
+ controlOnboardingSeen?: boolean;
11
19
  }
12
20
  export interface DogfoodFlowSnapshot {
13
21
  phase: 'idle' | 'denied' | 'auth-required' | 'machine-required' | 'opening' | 'error';
@@ -44,6 +52,19 @@ export interface SDKDogfoodConfig {
44
52
  appShortcut?: boolean | {
45
53
  label?: string;
46
54
  };
55
+ /** Smart in-app quick controls. On supported standalone iOS/Android builds,
56
+ * a three-finger hold opens a two-action card with no persistent overlay.
57
+ * Unsupported/accessibility-conflicted devices may show a minimized,
58
+ * draggable Y fallback. Yaver host/container mode suppresses both. */
59
+ controlGesture?: boolean | {
60
+ /** Hold duration, clamped to 650–2000 ms. Default 900 ms. */
61
+ durationMs?: number;
62
+ /** Default `minimized-y`; use `none` when Settings is the only fallback. */
63
+ fallback?: 'minimized-y' | 'none';
64
+ /** Initial user-facing presentation before their persisted SDK preference
65
+ * exists. `auto` keeps pixels clear when the gesture works. */
66
+ defaultPresentation?: 'auto' | 'minimized-y';
67
+ };
47
68
  }
48
69
  export interface SDKDogfoodStatus {
49
70
  active: boolean;
package/dist/index.d.ts CHANGED
@@ -28,7 +28,7 @@
28
28
  * ```
29
29
  */
30
30
  export { YaverFeedback } from './YaverFeedback';
31
- export type { DogfoodOnboardingOptions, DogfoodFlowPhase, DogfoodFlowState } from './YaverFeedback';
31
+ export type { DogfoodOnboardingOptions, DogfoodFlowPhase, DogfoodFlowState, DogfoodControlTriggerState, } from './YaverFeedback';
32
32
  export { captureStoreScreenshots } from './storeShots';
33
33
  export type { CaptureStoreScreenshotsOptions, CaptureStoreScreenshotsResult, StoreShotFrame, } from './storeShots';
34
34
  export { BlackBox } from './BlackBox';
@@ -59,7 +59,10 @@ export { YaverDeviceDogfood } from './deviceDogfood';
59
59
  export type { DeviceDogfoodOptions, DeviceDogfoodSession, DeviceDogfoodState } from './deviceDogfood';
60
60
  export { DogfoodController, DogfoodRuntimeError, defaultDogfoodLane, dogfoodLaneOptions, dogfoodLogLinesFromDevEvent, runtimeLogLinesFromDevEvent, validateDogfoodProject, } from './DogfoodRuntime';
61
61
  export type { DogfoodControllerOptions, DogfoodDriver, DogfoodFailure, DogfoodLane, DogfoodLaneOption, DogfoodLogLine, DogfoodPhase, DogfoodProject, DogfoodResult, DogfoodRunContext, DogfoodSnapshot, } from './DogfoodRuntime';
62
+ export { DogfoodLanePicker, DogfoodLiveConsole, DogfoodStatusRail } from './DogfoodSessionUi';
63
+ export type { DogfoodStatusStep, DogfoodStatusTone, DogfoodUiColors, } from './DogfoodSessionUi';
62
64
  export { FeedbackModal } from './FeedbackModal';
65
+ export { DogfoodQuickControls } from './DogfoodQuickControls';
63
66
  export { QuickActionIcon } from './QuickActionIcon';
64
67
  export type { QuickActionIconProps } from './QuickActionIcon';
65
68
  export { FixReport } from './FixReport';
package/dist/index.js CHANGED
@@ -29,8 +29,8 @@
29
29
  * ```
30
30
  */
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.setPreferredDogfoodLane = exports.getPreferredDogfoodLane = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.FeedbackModal = exports.validateDogfoodProject = exports.runtimeLogLinesFromDevEvent = exports.dogfoodLogLinesFromDevEvent = exports.dogfoodLaneOptions = exports.defaultDogfoodLane = exports.DogfoodRuntimeError = exports.DogfoodController = exports.YaverDeviceDogfood = exports.resolveSDKDogfood = exports.isYaverModeBadgeHidden = exports.showYaverModeBadge = exports.hideYaverModeBadge = exports.YaverModeBadge = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.RELOAD_APP_PATH = exports.RELOAD_PATH = exports.describeReloadFailure = exports.reloadFrameworkFamily = exports.reloadRequest = exports.reloadActions = exports.createP2PDogfoodDriver = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.captureStoreScreenshots = exports.YaverFeedback = void 0;
33
- 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.getDogfoodAccountAccess = exports.getSelectedDeviceId = void 0;
32
+ exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.setPreferredDogfoodLane = exports.getPreferredDogfoodLane = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.DogfoodQuickControls = exports.FeedbackModal = exports.DogfoodStatusRail = exports.DogfoodLiveConsole = exports.DogfoodLanePicker = exports.validateDogfoodProject = exports.runtimeLogLinesFromDevEvent = exports.dogfoodLogLinesFromDevEvent = exports.dogfoodLaneOptions = exports.defaultDogfoodLane = exports.DogfoodRuntimeError = exports.DogfoodController = exports.YaverDeviceDogfood = exports.resolveSDKDogfood = exports.isYaverModeBadgeHidden = exports.showYaverModeBadge = exports.hideYaverModeBadge = exports.YaverModeBadge = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.RELOAD_APP_PATH = exports.RELOAD_PATH = exports.describeReloadFailure = exports.reloadFrameworkFamily = exports.reloadRequest = exports.reloadActions = exports.createP2PDogfoodDriver = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.captureStoreScreenshots = exports.YaverFeedback = void 0;
33
+ 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.getDogfoodAccountAccess = exports.getSelectedDeviceId = exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = void 0;
34
34
  var YaverFeedback_1 = require("./YaverFeedback");
35
35
  Object.defineProperty(exports, "YaverFeedback", { enumerable: true, get: function () { return YaverFeedback_1.YaverFeedback; } });
36
36
  var storeShots_1 = require("./storeShots");
@@ -88,8 +88,14 @@ Object.defineProperty(exports, "dogfoodLaneOptions", { enumerable: true, get: fu
88
88
  Object.defineProperty(exports, "dogfoodLogLinesFromDevEvent", { enumerable: true, get: function () { return DogfoodRuntime_1.dogfoodLogLinesFromDevEvent; } });
89
89
  Object.defineProperty(exports, "runtimeLogLinesFromDevEvent", { enumerable: true, get: function () { return DogfoodRuntime_1.runtimeLogLinesFromDevEvent; } });
90
90
  Object.defineProperty(exports, "validateDogfoodProject", { enumerable: true, get: function () { return DogfoodRuntime_1.validateDogfoodProject; } });
91
+ var DogfoodSessionUi_1 = require("./DogfoodSessionUi");
92
+ Object.defineProperty(exports, "DogfoodLanePicker", { enumerable: true, get: function () { return DogfoodSessionUi_1.DogfoodLanePicker; } });
93
+ Object.defineProperty(exports, "DogfoodLiveConsole", { enumerable: true, get: function () { return DogfoodSessionUi_1.DogfoodLiveConsole; } });
94
+ Object.defineProperty(exports, "DogfoodStatusRail", { enumerable: true, get: function () { return DogfoodSessionUi_1.DogfoodStatusRail; } });
91
95
  var FeedbackModal_1 = require("./FeedbackModal");
92
96
  Object.defineProperty(exports, "FeedbackModal", { enumerable: true, get: function () { return FeedbackModal_1.FeedbackModal; } });
97
+ var DogfoodQuickControls_1 = require("./DogfoodQuickControls");
98
+ Object.defineProperty(exports, "DogfoodQuickControls", { enumerable: true, get: function () { return DogfoodQuickControls_1.DogfoodQuickControls; } });
93
99
  var QuickActionIcon_1 = require("./QuickActionIcon");
94
100
  Object.defineProperty(exports, "QuickActionIcon", { enumerable: true, get: function () { return QuickActionIcon_1.QuickActionIcon; } });
95
101
  var FixReport_1 = require("./FixReport");
@@ -12,6 +12,19 @@
12
12
  * still works (it just can't remember the disable beyond the
13
13
  * in-memory session).
14
14
  */
15
+ export type DogfoodControlPresentation = 'auto' | 'minimized-y';
16
+ export type DogfoodControlEdge = 'left' | 'right';
17
+ export interface DogfoodControlPosition {
18
+ edge: DogfoodControlEdge;
19
+ /** 0–1 within the safe vertical travel area. */
20
+ yRatio: number;
21
+ }
22
+ export declare function getDogfoodControlPresentation(scope?: string): Promise<DogfoodControlPresentation | null>;
23
+ export declare function setDogfoodControlPresentation(value: DogfoodControlPresentation, scope?: string): Promise<void>;
24
+ export declare function getDogfoodControlPosition(orientation: 'portrait' | 'landscape', scope?: string): Promise<DogfoodControlPosition | null>;
25
+ export declare function setDogfoodControlPosition(orientation: 'portrait' | 'landscape', position: DogfoodControlPosition, scope?: string): Promise<void>;
26
+ export declare function getDogfoodControlOnboardingSeen(scope?: string): Promise<boolean>;
27
+ export declare function setDogfoodControlOnboardingSeen(seen: boolean, scope?: string): Promise<void>;
15
28
  export type QuickIconColorPreset = 'orange' | 'lime' | 'cyan' | 'pink' | 'yellow' | 'slate';
16
29
  export declare const QUICK_ICON_COLOR_PRESETS: Record<QuickIconColorPreset, {
17
30
  label: string;
@@ -15,6 +15,12 @@
15
15
  */
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.QUICK_ICON_COLOR_PRESETS = void 0;
18
+ exports.getDogfoodControlPresentation = getDogfoodControlPresentation;
19
+ exports.setDogfoodControlPresentation = setDogfoodControlPresentation;
20
+ exports.getDogfoodControlPosition = getDogfoodControlPosition;
21
+ exports.setDogfoodControlPosition = setDogfoodControlPosition;
22
+ exports.getDogfoodControlOnboardingSeen = getDogfoodControlOnboardingSeen;
23
+ exports.setDogfoodControlOnboardingSeen = setDogfoodControlOnboardingSeen;
18
24
  exports.getQuickIconDisabled = getQuickIconDisabled;
19
25
  exports.setQuickIconDisabled = setQuickIconDisabled;
20
26
  exports.clearQuickIconDisabled = clearQuickIconDisabled;
@@ -36,6 +42,84 @@ catch {
36
42
  }
37
43
  const QUICK_ICON_DISABLED_KEY = 'yaver_feedback_quickicon_disabled';
38
44
  const QUICK_ICON_COLOR_KEY = 'yaver_feedback_quickicon_color';
45
+ const DOGFOOD_CONTROL_PRESENTATION_KEY = 'yaver_dogfood_control_presentation';
46
+ const DOGFOOD_CONTROL_POSITION_PREFIX = 'yaver_dogfood_control_position_';
47
+ const DOGFOOD_CONTROL_ONBOARDING_PREFIX = 'yaver_dogfood_control_onboarding_';
48
+ function dogfoodPreferenceKey(base, scope) {
49
+ const normalized = String(scope || 'legacy').trim() || 'legacy';
50
+ return `${base}_${encodeURIComponent(normalized)}`;
51
+ }
52
+ async function getDogfoodControlPresentation(scope) {
53
+ if (!AsyncStorage)
54
+ return null;
55
+ try {
56
+ const value = await AsyncStorage.getItem(dogfoodPreferenceKey(DOGFOOD_CONTROL_PRESENTATION_KEY, scope));
57
+ return value === 'auto' || value === 'minimized-y' ? value : null;
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ }
63
+ async function setDogfoodControlPresentation(value, scope) {
64
+ if (!AsyncStorage)
65
+ return;
66
+ try {
67
+ await AsyncStorage.setItem(dogfoodPreferenceKey(DOGFOOD_CONTROL_PRESENTATION_KEY, scope), value);
68
+ }
69
+ catch {
70
+ // best-effort
71
+ }
72
+ }
73
+ async function getDogfoodControlPosition(orientation, scope) {
74
+ if (!AsyncStorage)
75
+ return null;
76
+ try {
77
+ const raw = await AsyncStorage.getItem(dogfoodPreferenceKey(`${DOGFOOD_CONTROL_POSITION_PREFIX}${orientation}`, scope));
78
+ if (!raw)
79
+ return null;
80
+ const parsed = JSON.parse(raw);
81
+ if ((parsed.edge !== 'left' && parsed.edge !== 'right') || typeof parsed.yRatio !== 'number')
82
+ return null;
83
+ return { edge: parsed.edge, yRatio: Math.max(0, Math.min(1, parsed.yRatio)) };
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ async function setDogfoodControlPosition(orientation, position, scope) {
90
+ if (!AsyncStorage)
91
+ return;
92
+ try {
93
+ await AsyncStorage.setItem(dogfoodPreferenceKey(`${DOGFOOD_CONTROL_POSITION_PREFIX}${orientation}`, scope), JSON.stringify(position));
94
+ }
95
+ catch {
96
+ // best-effort
97
+ }
98
+ }
99
+ async function getDogfoodControlOnboardingSeen(scope) {
100
+ if (!AsyncStorage)
101
+ return false;
102
+ try {
103
+ return await AsyncStorage.getItem(dogfoodPreferenceKey(DOGFOOD_CONTROL_ONBOARDING_PREFIX, scope)) === '1';
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ async function setDogfoodControlOnboardingSeen(seen, scope) {
110
+ if (!AsyncStorage)
111
+ return;
112
+ try {
113
+ const key = dogfoodPreferenceKey(DOGFOOD_CONTROL_ONBOARDING_PREFIX, scope);
114
+ if (seen)
115
+ await AsyncStorage.setItem(key, '1');
116
+ else
117
+ await AsyncStorage.removeItem(key);
118
+ }
119
+ catch {
120
+ // best-effort startup cache; Convex remains authoritative.
121
+ }
122
+ }
39
123
  exports.QUICK_ICON_COLOR_PRESETS = {
40
124
  orange: {
41
125
  label: 'Orange',
@@ -0,0 +1,14 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RCT_EXTERN_MODULE(YaverDogfoodGesture, RCTEventEmitter)
5
+
6
+ RCT_EXTERN_METHOD(getCapability:(RCTPromiseResolveBlock)resolve
7
+ rejecter:(RCTPromiseRejectBlock)reject)
8
+
9
+ RCT_EXTERN_METHOD(setEnabled:(BOOL)enabled
10
+ durationMs:(double)durationMs
11
+ resolver:(RCTPromiseResolveBlock)resolve
12
+ rejecter:(RCTPromiseRejectBlock)reject)
13
+
14
+ @end