yaver-feedback-react-native 0.9.2 → 0.9.3

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.
Files changed (103) hide show
  1. package/README.md +102 -1
  2. package/dist/AuthOverlay.d.ts +1 -14
  3. package/dist/AuthOverlay.js +9 -62
  4. package/dist/Discovery.js +0 -6
  5. package/dist/DogfoodRuntime.d.ts +124 -0
  6. package/dist/DogfoodRuntime.js +273 -0
  7. package/dist/FeedbackModal.js +347 -61
  8. package/dist/LoginScreen.d.ts +1 -5
  9. package/dist/LoginScreen.js +2 -6
  10. package/dist/MachinePickerScreen.d.ts +1 -3
  11. package/dist/MachinePickerScreen.js +6 -18
  12. package/dist/P2PClient.d.ts +116 -3
  13. package/dist/P2PClient.js +273 -3
  14. package/dist/P2PDogfoodDriver.d.ts +12 -0
  15. package/dist/P2PDogfoodDriver.js +118 -0
  16. package/dist/PairDeviceModal.d.ts +2 -3
  17. package/dist/VibeChatScreen.d.ts +11 -1
  18. package/dist/VibeChatScreen.js +200 -41
  19. package/dist/YaverFeedback.d.ts +34 -0
  20. package/dist/YaverFeedback.js +124 -19
  21. package/dist/YaverModeBadge.d.ts +22 -0
  22. package/dist/YaverModeBadge.js +219 -0
  23. package/dist/__tests__/AuthDevices.test.js +1 -48
  24. package/dist/__tests__/DogfoodRuntime.test.d.ts +1 -0
  25. package/dist/__tests__/DogfoodRuntime.test.js +116 -0
  26. package/dist/__tests__/P2PDogfoodDriver.test.d.ts +1 -0
  27. package/dist/__tests__/P2PDogfoodDriver.test.js +64 -0
  28. package/dist/__tests__/ReportIdentity.test.d.ts +25 -1
  29. package/dist/__tests__/ReportIdentity.test.js +34 -22
  30. package/dist/__tests__/YaverFeedback.test.js +13 -3
  31. package/dist/__tests__/deviceDogfood.test.d.ts +1 -0
  32. package/dist/__tests__/deviceDogfood.test.js +86 -0
  33. package/dist/__tests__/dogfoodPolicy.test.d.ts +1 -0
  34. package/dist/__tests__/dogfoodPolicy.test.js +33 -0
  35. package/dist/_core/ansi.d.ts +117 -0
  36. package/dist/_core/ansi.js +468 -0
  37. package/dist/_core/ansi.test.d.ts +1 -0
  38. package/dist/_core/ansi.test.js +225 -0
  39. package/dist/_core/buildFeedbackPrompt.d.ts +4 -9
  40. package/dist/_core/buildFeedbackPrompt.js +18 -72
  41. package/dist/_core/constants.d.ts +19 -6
  42. package/dist/_core/constants.js +20 -7
  43. package/dist/_core/device.d.ts +9 -23
  44. package/dist/_core/device.js +13 -28
  45. package/dist/_core/endpoints.d.ts +0 -7
  46. package/dist/_core/endpoints.js +0 -7
  47. package/dist/_core/index.d.ts +4 -0
  48. package/dist/_core/index.js +4 -0
  49. package/dist/_core/remoteless.d.ts +44 -0
  50. package/dist/_core/remoteless.js +75 -0
  51. package/dist/_core/trace.d.ts +47 -0
  52. package/dist/_core/trace.js +38 -0
  53. package/dist/_core/trace.test.d.ts +1 -0
  54. package/dist/_core/trace.test.js +60 -0
  55. package/dist/auth.d.ts +3 -60
  56. package/dist/auth.js +6 -88
  57. package/dist/deviceDogfood.d.ts +53 -0
  58. package/dist/deviceDogfood.js +137 -0
  59. package/dist/dogfoodPolicy.d.ts +25 -0
  60. package/dist/dogfoodPolicy.js +24 -0
  61. package/dist/index.d.ts +13 -4
  62. package/dist/index.js +24 -8
  63. package/dist/reloadActions.js +2 -2
  64. package/dist/types.d.ts +50 -7
  65. package/package.json +12 -3
  66. package/src/AuthOverlay.tsx +20 -106
  67. package/src/Discovery.ts +0 -6
  68. package/src/DogfoodRuntime.ts +373 -0
  69. package/src/FeedbackModal.tsx +445 -67
  70. package/src/LoginScreen.tsx +2 -22
  71. package/src/MachinePickerScreen.tsx +6 -21
  72. package/src/P2PClient.ts +347 -4
  73. package/src/P2PDogfoodDriver.ts +132 -0
  74. package/src/PairDeviceModal.tsx +2 -3
  75. package/src/VibeChatScreen.tsx +232 -42
  76. package/src/YaverFeedback.ts +133 -22
  77. package/src/YaverModeBadge.tsx +234 -0
  78. package/src/__tests__/AuthDevices.test.ts +1 -52
  79. package/src/__tests__/DogfoodRuntime.test.ts +135 -0
  80. package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
  81. package/src/__tests__/ReportIdentity.test.ts +36 -27
  82. package/src/__tests__/YaverFeedback.test.ts +15 -3
  83. package/src/__tests__/deviceDogfood.test.ts +77 -0
  84. package/src/__tests__/dogfoodPolicy.test.ts +34 -0
  85. package/src/_core/ansi.test.ts +250 -0
  86. package/src/_core/ansi.ts +475 -0
  87. package/src/_core/buildFeedbackPrompt.ts +18 -95
  88. package/src/_core/constants.ts +20 -6
  89. package/src/_core/device.ts +13 -30
  90. package/src/_core/endpoints.ts +0 -7
  91. package/src/_core/index.ts +4 -0
  92. package/src/_core/remoteless.ts +110 -0
  93. package/src/_core/trace.test.ts +58 -0
  94. package/src/_core/trace.ts +75 -0
  95. package/src/auth.ts +7 -156
  96. package/src/deviceDogfood.ts +171 -0
  97. package/src/dogfoodPolicy.ts +40 -0
  98. package/src/index.ts +40 -11
  99. package/src/reloadActions.ts +2 -2
  100. package/src/types.ts +45 -7
  101. package/dist/GuestOnboardingScreen.d.ts +0 -8
  102. package/dist/GuestOnboardingScreen.js +0 -282
  103. package/src/GuestOnboardingScreen.tsx +0 -307
@@ -42,11 +42,14 @@ const upload_1 = require("./upload");
42
42
  const P2PClient_1 = require("./P2PClient");
43
43
  const AuthOverlay_1 = require("./AuthOverlay");
44
44
  const QuickActionIcon_1 = require("./QuickActionIcon");
45
+ const YaverModeBadge_1 = require("./YaverModeBadge");
45
46
  const VibeChatScreen_1 = require("./VibeChatScreen");
46
47
  const DeployPanel_1 = require("./DeployPanel");
47
48
  const auth_1 = require("./auth");
48
49
  const reloadActions_1 = require("./reloadActions");
49
50
  const preferences_1 = require("./preferences");
51
+ const DogfoodRuntime_1 = require("./DogfoodRuntime");
52
+ const P2PDogfoodDriver_1 = require("./P2PDogfoodDriver");
50
53
  const PRIMARY_RUNNER_IDS = ['claude', 'codex', 'opencode'];
51
54
  function normalizeRunnerStatusRows(rows) {
52
55
  const byId = new Map();
@@ -85,6 +88,7 @@ function normalizeRunnerStatusRows(rows) {
85
88
  version: row.version,
86
89
  tone: 'warning',
87
90
  statusLine: 'Not installed on the selected machine',
91
+ models: row.models,
88
92
  detail,
89
93
  };
90
94
  }
@@ -101,6 +105,7 @@ function normalizeRunnerStatusRows(rows) {
101
105
  statusLine: configured
102
106
  ? `${versionPrefix}Configured on the selected machine`
103
107
  : `${versionPrefix}Needs provider config on the selected machine`,
108
+ models: row.models,
104
109
  detail,
105
110
  };
106
111
  }
@@ -116,12 +121,34 @@ function normalizeRunnerStatusRows(rows) {
116
121
  statusLine: authed
117
122
  ? `${versionPrefix}Signed in on the selected machine`
118
123
  : `${versionPrefix}Not signed in on the selected machine`,
124
+ models: row.models,
119
125
  detail,
120
126
  actionLabel: authed ? 'Re-auth' : 'Sign in',
121
127
  actionRunner: id,
122
128
  };
123
129
  });
124
130
  }
131
+ /**
132
+ * Renders the "inside Yaver" mark unless the app opted out.
133
+ *
134
+ * Lives here rather than in YaverModeBadge so the component stays a dumb view
135
+ * and the DEFAULT (`modeBadge !== false` → on) is expressed in exactly one
136
+ * place. Mounted from FeedbackModal because that is the component integrating
137
+ * apps already render — a default that required a new mount point would not be
138
+ * a default, it would be a feature request.
139
+ */
140
+ const YaverModeBadgeGate = () => {
141
+ const [, refresh] = (0, react_1.useState)(0);
142
+ (0, react_1.useEffect)(() => {
143
+ const sub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:dogfoodChanged', () => refresh((n) => n + 1));
144
+ return () => sub.remove();
145
+ }, []);
146
+ const cfg = YaverFeedback_1.YaverFeedback.getConfig();
147
+ if (cfg?.modeBadge === false)
148
+ return null;
149
+ const dogfood = YaverFeedback_1.YaverFeedback.getDogfoodStatus();
150
+ return (<YaverModeBadge_1.YaverModeBadge position={cfg?.modeBadgePosition ?? 'bottom-left'} force={dogfood.active} dogfood={dogfood.active} dogfoodLabel={dogfood.label} onExitDogfood={() => YaverFeedback_1.YaverFeedback.exitDogfoodMode()}/>);
151
+ };
125
152
  const FeedbackModal = () => {
126
153
  const { width: winW, height: winH } = (0, react_native_1.useWindowDimensions)();
127
154
  const isTablet = Math.min(winW, winH) >= 600;
@@ -129,6 +156,7 @@ const FeedbackModal = () => {
129
156
  // looks empty on a 1024pt iPad. Mobile keeps 3-col.
130
157
  const iconOptionWidthOverride = isTablet ? '18%' : undefined;
131
158
  const [visible, setVisible] = (0, react_1.useState)(false);
159
+ const [dogfoodActive, setDogfoodActive] = (0, react_1.useState)(() => YaverFeedback_1.YaverFeedback.getDogfoodStatus().active);
132
160
  const [action, setAction] = (0, react_1.useState)('idle');
133
161
  const [error, setError] = (0, react_1.useState)(null);
134
162
  const [toast, setToast] = (0, react_1.useState)(null);
@@ -168,8 +196,93 @@ const FeedbackModal = () => {
168
196
  const [runnerStatusError, setRunnerStatusError] = (0, react_1.useState)(null);
169
197
  const [preferredRunner, setPreferredRunnerState] = (0, react_1.useState)(null);
170
198
  const [preferredModel, setPreferredModelState] = (0, react_1.useState)('');
199
+ const [activeTab, setActiveTab] = (0, react_1.useState)('chat');
171
200
  const [showOpenCodeConfig, setShowOpenCodeConfig] = (0, react_1.useState)(false);
201
+ const scrollRef = (0, react_1.useRef)(null);
202
+ const [dogfoodEnrollment, setDogfoodEnrollment] = (0, react_1.useState)(null);
203
+ const [dogfoodProjects, setDogfoodProjects] = (0, react_1.useState)([]);
204
+ const [dogfoodProject, setDogfoodProject] = (0, react_1.useState)(null);
205
+ const [dogfoodLane, setDogfoodLane] = (0, react_1.useState)('browser');
206
+ const [dogfoodNativeAvailable, setDogfoodNativeAvailable] = (0, react_1.useState)(false);
207
+ const [dogfoodRuntime, setDogfoodRuntime] = (0, react_1.useState)(null);
208
+ const [dogfoodSetupLoading, setDogfoodSetupLoading] = (0, react_1.useState)(false);
209
+ const dogfoodControllerRef = (0, react_1.useRef)(null);
172
210
  const mountedRef = (0, react_1.useRef)(true);
211
+ const loadDogfoodOnboarding = (0, react_1.useCallback)(async () => {
212
+ const onboarding = YaverFeedback_1.YaverFeedback.getDogfoodOnboarding();
213
+ if (!onboarding)
214
+ return;
215
+ setDogfoodSetupLoading(true);
216
+ setDogfoodEnrollment({ status: 'checking' });
217
+ try {
218
+ const enrolled = await YaverFeedback_1.YaverFeedback.enableDeviceDogfood(onboarding);
219
+ if (!mountedRef.current)
220
+ return;
221
+ setDogfoodEnrollment({ status: enrolled.status, installationId: enrolled.installationId });
222
+ if (enrolled.status !== 'active')
223
+ return;
224
+ let client = YaverFeedback_1.YaverFeedback.getP2PClient();
225
+ if (!client && await YaverFeedback_1.YaverFeedback.reconnect())
226
+ client = YaverFeedback_1.YaverFeedback.getP2PClient();
227
+ if (!client)
228
+ throw new Error('The selected Yaver machine is not reachable yet.');
229
+ const projects = await client.listDogfoodProjects();
230
+ if (!mountedRef.current)
231
+ return;
232
+ setDogfoodProjects(projects);
233
+ const hint = String(onboarding.projectName || '').trim().toLowerCase();
234
+ const preferred = projects.find((item) => item.name.toLowerCase() === hint)
235
+ || projects.find((item) => hint && item.name.toLowerCase().includes(hint))
236
+ || projects[0]
237
+ || null;
238
+ setDogfoodProject((current) => current && projects.some((item) => item.path === current.path) ? current : preferred);
239
+ if (preferred)
240
+ setDogfoodLane((0, DogfoodRuntime_1.defaultDogfoodLane)(preferred.framework || onboarding.framework || 'expo'));
241
+ }
242
+ catch (cause) {
243
+ if (mountedRef.current)
244
+ setDogfoodEnrollment({ status: 'failed', error: cause instanceof Error ? cause.message : String(cause) });
245
+ }
246
+ finally {
247
+ if (mountedRef.current)
248
+ setDogfoodSetupLoading(false);
249
+ }
250
+ }, []);
251
+ (0, react_1.useEffect)(() => {
252
+ let cancelled = false;
253
+ setDogfoodNativeAvailable(false);
254
+ const client = YaverFeedback_1.YaverFeedback.getP2PClient();
255
+ if (!client || !dogfoodProject)
256
+ return () => { cancelled = true; };
257
+ const framework = dogfoodProject.framework || YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()?.framework || 'expo';
258
+ void client.getDogfoodRemoteRuntimeCapabilities(dogfoodProject.path, framework)
259
+ .then((value) => {
260
+ if (!cancelled)
261
+ setDogfoodNativeAvailable(value.targets.some((target) => target.enabled && target.id !== 'browser-window'));
262
+ })
263
+ .catch(() => { });
264
+ return () => { cancelled = true; };
265
+ }, [dogfoodProject]);
266
+ const startDogfoodRuntime = (0, react_1.useCallback)(async () => {
267
+ const client = YaverFeedback_1.YaverFeedback.getP2PClient();
268
+ const onboarding = YaverFeedback_1.YaverFeedback.getDogfoodOnboarding();
269
+ if (!client || !dogfoodProject || !onboarding)
270
+ return;
271
+ await dogfoodControllerRef.current?.stop().catch(() => { });
272
+ const framework = dogfoodProject.framework || onboarding.framework || 'expo';
273
+ const controller = new DogfoodRuntime_1.DogfoodController({
274
+ name: dogfoodProject.name,
275
+ workDir: dogfoodProject.path,
276
+ framework,
277
+ lane: dogfoodLane,
278
+ }, (0, P2PDogfoodDriver_1.createP2PDogfoodDriver)(client), {
279
+ onChange: (snapshot) => { if (mountedRef.current)
280
+ setDogfoodRuntime(snapshot); },
281
+ });
282
+ dogfoodControllerRef.current = controller;
283
+ setDogfoodRuntime(controller.snapshot());
284
+ await controller.trigger().catch(() => { });
285
+ }, [dogfoodLane, dogfoodProject]);
173
286
  /**
174
287
  * Ask the machine what its dev server is doing, so the reload actions can
175
288
  * be enabled/disabled against reality rather than against a guess.
@@ -225,8 +338,7 @@ const FeedbackModal = () => {
225
338
  }
226
339
  try {
227
340
  const devices = await (0, auth_1.listReachableDevices)(cfg.authToken);
228
- const all = [...devices.owned, ...devices.shared];
229
- const device = all.find((candidate) => candidate.deviceId === cfg.preferredDeviceId) ?? null;
341
+ const device = devices.owned.find((candidate) => candidate.deviceId === cfg.preferredDeviceId) ?? null;
230
342
  if (!device) {
231
343
  if (mountedRef.current) {
232
344
  setMachineCard({
@@ -245,9 +357,8 @@ const FeedbackModal = () => {
245
357
  if (device.isOnline && !device.needsAuth && client) {
246
358
  reachable = await client.health();
247
359
  }
248
- const hostHint = device.hostEmail ? ` via ${device.hostEmail}` : '';
249
360
  let status = 'live';
250
- let detail = `${device.platform}${hostHint}`;
361
+ let detail = device.platform;
251
362
  if (!device.isOnline) {
252
363
  status = 'offline';
253
364
  detail = 'Machine offline. Start `yaver serve` on the selected machine.';
@@ -320,7 +431,7 @@ const FeedbackModal = () => {
320
431
  if (!client) {
321
432
  throw new Error('Not connected to the selected machine yet.');
322
433
  }
323
- const rows = await client.getRunnerAuthStatus();
434
+ const rows = await client.getAvailableRunners();
324
435
  if (mountedRef.current) {
325
436
  setRunnerCards(normalizeRunnerStatusRows(rows));
326
437
  }
@@ -358,13 +469,20 @@ const FeedbackModal = () => {
358
469
  mountedRef.current = true;
359
470
  const sub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startReport', () => {
360
471
  if (YaverFeedback_1.YaverFeedback.isEnabled()) {
472
+ const onboarding = YaverFeedback_1.YaverFeedback.getDogfoodOnboarding();
473
+ const directDogfood = YaverFeedback_1.YaverFeedback.getDogfoodStatus().active;
474
+ setDogfoodActive(directDogfood);
361
475
  setVisible(true);
362
476
  setError(null);
363
477
  setToast(null);
364
478
  setProgress(null);
365
479
  setAction('idle');
366
- setShowVibeInput(false);
480
+ setShowVibeInput(directDogfood);
367
481
  setVibePrompt('');
482
+ if (onboarding) {
483
+ setActiveTab('settings');
484
+ void loadDogfoodOnboarding();
485
+ }
368
486
  // Re-read the "user hid the quick icon" flag on every open so
369
487
  // the re-enable row reflects the latest preference (the user
370
488
  // might have hidden or shown it between opens).
@@ -384,6 +502,13 @@ const FeedbackModal = () => {
384
502
  void loadRunnerStatuses();
385
503
  }
386
504
  });
505
+ const dogfoodSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:dogfoodChanged', (payload) => {
506
+ if (!mountedRef.current)
507
+ return;
508
+ setDogfoodActive(payload?.active === true);
509
+ if (payload?.exited === true)
510
+ setVisible(false);
511
+ });
387
512
  // Agent streams build / compile progress through the BlackBox
388
513
  // SSE command channel as `command: "status"`; YaverFeedback re-emits
389
514
  // it as `yaverFeedback:status`. Show the most recent message in the
@@ -407,9 +532,10 @@ const FeedbackModal = () => {
407
532
  return () => {
408
533
  mountedRef.current = false;
409
534
  sub.remove();
535
+ dogfoodSub.remove();
410
536
  statusSub.remove();
411
537
  };
412
- }, [loadRunnerStatuses, loadSelectedMachine]);
538
+ }, [loadDogfoodOnboarding, loadRunnerStatuses, loadSelectedMachine]);
413
539
  (0, react_1.useEffect)(() => {
414
540
  if (!visible)
415
541
  return;
@@ -443,6 +569,12 @@ const FeedbackModal = () => {
443
569
  hideSub.remove();
444
570
  };
445
571
  }, [visible]);
572
+ (0, react_1.useEffect)(() => {
573
+ if (!visible || !showVibeInput)
574
+ return;
575
+ const timer = setTimeout(() => scrollRef.current?.scrollToEnd({ animated: true }), keyboardInset > 0 ? 120 : 40);
576
+ return () => clearTimeout(timer);
577
+ }, [keyboardInset, showVibeInput, visible]);
446
578
  const closeSoon = (0, react_1.useCallback)((delayMs = 1200) => {
447
579
  setTimeout(() => {
448
580
  if (mountedRef.current)
@@ -458,6 +590,9 @@ const FeedbackModal = () => {
458
590
  setShowVibeInput(false);
459
591
  setVibePrompt('');
460
592
  setRunnerStatusError(null);
593
+ void dogfoodControllerRef.current?.stop().catch(() => { });
594
+ dogfoodControllerRef.current = null;
595
+ YaverFeedback_1.YaverFeedback.clearDogfoodOnboarding();
461
596
  }, []);
462
597
  // Helper: run a P2P call; on network failure, ask YaverFeedback to
463
598
  // re-query Convex for the fresh IP and retry once. Solves the common
@@ -555,8 +690,11 @@ const FeedbackModal = () => {
555
690
  throw new Error('Selected machine is offline. Start `yaver serve` on it first.');
556
691
  }
557
692
  let ackMessage = `${reloadAction.label} requested.`;
558
- await runWithReconnect(async (client) => {
559
- const ack = await client.reloadWithMode(reloadAction.mode, devSnapshot);
693
+ await runWithReconnect(async () => {
694
+ const renderClient = YaverFeedback_1.YaverFeedback.getRenderP2PClient();
695
+ if (!renderClient)
696
+ throw new Error('Render machine is not connected yet.');
697
+ const ack = await renderClient.reloadWithMode(reloadAction.mode, devSnapshot);
560
698
  ackMessage = ack.message;
561
699
  setToast(ack.message);
562
700
  setProgress(0.2);
@@ -608,8 +746,11 @@ const FeedbackModal = () => {
608
746
  // map this app to its MobileProject scan entry without needing
609
747
  // `yaver dev start` to have been run.
610
748
  let ackMessage = 'Reload request acknowledged.';
611
- await runWithReconnect(async (client) => {
612
- const ack = await client.reloadApp('bundle');
749
+ await runWithReconnect(async () => {
750
+ const renderClient = YaverFeedback_1.YaverFeedback.getRenderP2PClient();
751
+ if (!renderClient)
752
+ throw new Error('Render machine is not connected yet.');
753
+ const ack = await renderClient.reloadApp('bundle');
613
754
  ackMessage = ack.message;
614
755
  setToast(ack.message);
615
756
  setProgress(0.2);
@@ -837,6 +978,7 @@ const FeedbackModal = () => {
837
978
  taskId: result.taskId,
838
979
  initialPrompt: promptText,
839
980
  project: identity.projectName,
981
+ projectPath: identity.projectPath,
840
982
  runner: preferredRunner ?? undefined,
841
983
  model: preferredModel ?? undefined,
842
984
  });
@@ -857,6 +999,7 @@ const FeedbackModal = () => {
857
999
  }, [closeSoon, isRecordingVideo, lastVideo]);
858
1000
  */
859
1001
  const busy = action !== 'idle';
1002
+ const machineRouting = YaverFeedback_1.YaverFeedback.getMachineRouting();
860
1003
  const readyRunnerCount = runnerCards.filter((row) => row.ready || row.authConfigured).length;
861
1004
  const missingRunnerCount = runnerCards.filter((row) => !row.installed).length;
862
1005
  const needsAuthRunnerCount = runnerCards.filter((row) => row.installed && !row.authConfigured && !row.ready).length;
@@ -864,14 +1007,18 @@ const FeedbackModal = () => {
864
1007
  // for the live chat screen. The chat manages its own SSE
865
1008
  // subscription, multi-turn follow-ups, and Reload button. Closing
866
1009
  // the chat returns to idle and clears the active vibe.
867
- if (visible && activeVibe) {
1010
+ if (activeVibe) {
868
1011
  const client = YaverFeedback_1.YaverFeedback.getP2PClient();
869
1012
  return (<>
870
1013
  <AuthOverlay_1.AuthOverlay />
871
1014
  <QuickActionIcon_1.QuickActionIcon />
872
- <react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={() => setActiveVibe(null)}>
873
- {client ? (<VibeChatScreen_1.VibeChatScreen client={client} initialTaskId={activeVibe.taskId} initialUserPrompt={activeVibe.initialPrompt} project={activeVibe.project} runner={activeVibe.runner} model={activeVibe.model} onClose={() => setActiveVibe(null)} onReload={async () => {
874
- const c = YaverFeedback_1.YaverFeedback.getP2PClient();
1015
+ <YaverModeBadgeGate />
1016
+ <react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={handleClose}>
1017
+ {client ? (<VibeChatScreen_1.VibeChatScreen client={client} initialTaskId={activeVibe.taskId} initialUserPrompt={activeVibe.initialPrompt} project={activeVibe.project} projectPath={activeVibe.projectPath} runner={activeVibe.runner} model={activeVibe.model} voiceInputEnabled={YaverFeedback_1.YaverFeedback.getConfig()?.voiceInputEnabled === true} onClose={() => setActiveVibe(null)} onNewTopic={() => {
1018
+ setActiveVibe(null);
1019
+ setShowVibeInput(true);
1020
+ }} onMinimize={handleClose} codingMachine={YaverFeedback_1.YaverFeedback.getMachineRouting().codingDeviceId} renderMachine={YaverFeedback_1.YaverFeedback.getMachineRouting().renderDeviceId} onReload={async () => {
1021
+ const c = YaverFeedback_1.YaverFeedback.getRenderP2PClient();
875
1022
  if (!c)
876
1023
  throw new Error('Not connected');
877
1024
  await c.reloadApp();
@@ -882,6 +1029,7 @@ const FeedbackModal = () => {
882
1029
  return (<>
883
1030
  <AuthOverlay_1.AuthOverlay />
884
1031
  <QuickActionIcon_1.QuickActionIcon />
1032
+ <YaverModeBadgeGate />
885
1033
  {visible && (<react_native_1.Modal visible={visible} animationType="slide" transparent onRequestClose={handleClose}>
886
1034
  <react_native_1.Pressable style={styles.overlay} onPress={handleClose}>
887
1035
  <react_native_1.KeyboardAvoidingView behavior={react_native_1.Platform.OS === 'ios' ? 'padding' : 'height'} keyboardVerticalOffset={react_native_1.Platform.OS === 'ios' ? 12 : 0} style={styles.kbAvoider} pointerEvents="box-none">
@@ -904,19 +1052,80 @@ const FeedbackModal = () => {
904
1052
  e.stopPropagation();
905
1053
  react_native_1.Keyboard.dismiss();
906
1054
  }}>
907
- <react_native_1.ScrollView style={styles.scroll} contentContainerStyle={[
1055
+ <react_native_1.ScrollView ref={scrollRef} style={styles.scroll} contentContainerStyle={[
908
1056
  styles.scrollContent,
909
1057
  showVibeInput && keyboardInset > 0
910
1058
  ? { paddingBottom: 8 + keyboardInset }
911
1059
  : null,
912
- ]} keyboardShouldPersistTaps="handled" keyboardDismissMode={react_native_1.Platform.OS === 'ios' ? 'interactive' : 'on-drag'}>
1060
+ ]} keyboardShouldPersistTaps="handled" keyboardDismissMode={react_native_1.Platform.OS === 'ios' ? 'interactive' : 'on-drag'} contentInsetAdjustmentBehavior="always" automaticallyAdjustKeyboardInsets={react_native_1.Platform.OS === 'ios'}>
913
1061
  <react_native_1.View style={styles.header}>
914
- <react_native_1.Text style={styles.title}>Send Feedback</react_native_1.Text>
1062
+ <react_native_1.Text style={styles.title}>
1063
+ {dogfoodActive ? `${YaverFeedback_1.YaverFeedback.getDogfoodStatus().label || 'App'} Developer Mode` : 'Send Feedback'}
1064
+ </react_native_1.Text>
915
1065
  <react_native_1.Pressable onPress={handleClose} hitSlop={12} style={styles.closeBtn} accessibilityRole="button" accessibilityLabel="Close">
916
1066
  <react_native_1.Text style={styles.closeIcon}>×</react_native_1.Text>
917
1067
  </react_native_1.Pressable>
918
1068
  </react_native_1.View>
919
1069
 
1070
+ <react_native_1.View style={styles.tabs} accessibilityRole="tablist">
1071
+ {['chat', 'settings'].map((tab) => (<react_native_1.Pressable key={tab} onPress={() => setActiveTab(tab)} style={[styles.tab, activeTab === tab && styles.tabSelected]} accessibilityRole="tab" accessibilityState={{ selected: activeTab === tab }}>
1072
+ <react_native_1.Text style={[styles.tabText, activeTab === tab && styles.tabTextSelected]}>{tab === 'chat' ? 'Chat' : 'Settings'}</react_native_1.Text>
1073
+ </react_native_1.Pressable>))}
1074
+ </react_native_1.View>
1075
+
1076
+ <react_native_1.View style={[styles.tabContent, activeTab !== 'settings' && styles.hidden]}>
1077
+ <>
1078
+ {YaverFeedback_1.YaverFeedback.getDogfoodOnboarding() ? (<react_native_1.View style={styles.dogfoodWizard}>
1079
+ <react_native_1.Text style={styles.dogfoodWizardTitle}>Dogfood this app</react_native_1.Text>
1080
+ <react_native_1.Text style={styles.dogfoodWizardHint}>
1081
+ OAuth ✓ · machine {machineCard.device ? '✓' : 'required'} · installation {dogfoodEnrollment?.status || 'checking'}
1082
+ </react_native_1.Text>
1083
+ {dogfoodEnrollment?.installationId ? (<react_native_1.Text selectable style={styles.dogfoodInstallationId}>
1084
+ This device · {dogfoodEnrollment.installationId}
1085
+ </react_native_1.Text>) : null}
1086
+ {dogfoodEnrollment?.status !== 'active' ? (<react_native_1.View style={styles.dogfoodPendingBox}>
1087
+ <react_native_1.Text style={styles.dogfoodPendingText}>
1088
+ {dogfoodEnrollment?.error
1089
+ || 'Approve this installation from Yaver → Settings → Third-party app testing. A UUID alone never grants access.'}
1090
+ </react_native_1.Text>
1091
+ <react_native_1.Pressable onPress={() => void loadDogfoodOnboarding()} style={({ pressed }) => [styles.runnerRefreshBtn, pressed && styles.buttonPressed]}>
1092
+ <react_native_1.Text style={styles.runnerRefreshBtnText}>{dogfoodSetupLoading ? 'Checking…' : 'Check approval'}</react_native_1.Text>
1093
+ </react_native_1.Pressable>
1094
+ </react_native_1.View>) : (<>
1095
+ <react_native_1.Text style={styles.dogfoodStepLabel}>Project on selected machine</react_native_1.Text>
1096
+ <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.dogfoodChoiceRow}>
1097
+ {dogfoodProjects.map((project) => (<react_native_1.Pressable key={project.path} onPress={() => {
1098
+ setDogfoodProject(project);
1099
+ setDogfoodLane((0, DogfoodRuntime_1.defaultDogfoodLane)(project.framework || YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()?.framework || 'expo'));
1100
+ setDogfoodRuntime(null);
1101
+ }} style={[styles.dogfoodChoice, dogfoodProject?.path === project.path && styles.dogfoodChoiceSelected]}>
1102
+ <react_native_1.Text style={[styles.dogfoodChoiceText, dogfoodProject?.path === project.path && styles.dogfoodChoiceTextSelected]}>{project.name}</react_native_1.Text>
1103
+ </react_native_1.Pressable>))}
1104
+ </react_native_1.ScrollView>
1105
+ <react_native_1.Text style={styles.dogfoodStepLabel}>Runtime lane</react_native_1.Text>
1106
+ <react_native_1.View style={styles.dogfoodChoiceRow}>
1107
+ {(0, DogfoodRuntime_1.dogfoodLaneOptions)(dogfoodProject?.framework || YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()?.framework || 'expo', { nativeRuntimeAvailable: dogfoodNativeAvailable }).map((option) => (<react_native_1.Pressable key={option.lane} onPress={() => option.supported && setDogfoodLane(option.lane)} style={[
1108
+ styles.dogfoodChoice,
1109
+ dogfoodLane === option.lane && styles.dogfoodChoiceSelected,
1110
+ !option.supported && styles.actionBtnDisabled,
1111
+ ]} accessibilityState={{ disabled: !option.supported, selected: dogfoodLane === option.lane }}>
1112
+ <react_native_1.Text style={[styles.dogfoodChoiceText, dogfoodLane === option.lane && styles.dogfoodChoiceTextSelected]}>{option.label}</react_native_1.Text>
1113
+ </react_native_1.Pressable>))}
1114
+ </react_native_1.View>
1115
+ <react_native_1.Text style={styles.dogfoodWizardHint}>
1116
+ Runner: {[preferredRunner || 'automatic', preferredModel].filter(Boolean).join(' · ')}. Change it under Coding Agents below.
1117
+ </react_native_1.Text>
1118
+ <ActionRow label={dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase) ? dogfoodRuntime.message : 'Start Dogfood'} tint="#818cf8" onPress={() => void startDogfoodRuntime()} disabled={!dogfoodProject || !!(dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase))} busy={!!dogfoodRuntime && !['idle', 'ready', 'failed', 'stopped'].includes(dogfoodRuntime.phase)}/>
1119
+ {dogfoodRuntime ? (<react_native_1.View style={styles.dogfoodConsole}>
1120
+ <react_native_1.Text style={styles.dogfoodConsoleStatus}>{dogfoodRuntime.message}</react_native_1.Text>
1121
+ {dogfoodRuntime.logs.slice(-80).map((line, index) => (<react_native_1.Text key={`${line.at}-${index}`} selectable style={styles.dogfoodConsoleLine}>{line.text}</react_native_1.Text>))}
1122
+ {dogfoodRuntime.failure ? (<react_native_1.Text style={styles.dogfoodConsoleError}>{dogfoodRuntime.failure.error}{'\n'}{dogfoodRuntime.failure.remedy}</react_native_1.Text>) : null}
1123
+ {dogfoodRuntime.result?.url ? (<react_native_1.Pressable onPress={() => void react_native_1.Linking.openURL(dogfoodRuntime.result.url)} style={styles.dogfoodOpenPreview}>
1124
+ <react_native_1.Text style={styles.dogfoodOpenPreviewText}>Open dogfooded app</react_native_1.Text>
1125
+ </react_native_1.Pressable>) : null}
1126
+ </react_native_1.View>) : null}
1127
+ </>)}
1128
+ </react_native_1.View>) : null}
920
1129
  <react_native_1.Pressable onPress={() => {
921
1130
  if (!YaverFeedback_1.YaverFeedback.isAuthed()) {
922
1131
  YaverFeedback_1.YaverFeedback.showLogin();
@@ -937,7 +1146,7 @@ const FeedbackModal = () => {
937
1146
  machineCard.status === 'attention' && styles.machineDotAttention,
938
1147
  machineCard.status === 'offline' && styles.machineDotOffline,
939
1148
  ]}/>
940
- <react_native_1.Text style={styles.machineLabel}>Selected Machine</react_native_1.Text>
1149
+ <react_native_1.Text style={styles.machineLabel}>Coding Machine</react_native_1.Text>
941
1150
  </react_native_1.View>
942
1151
  <react_native_1.Text style={styles.machineAction}>
943
1152
  {machineCard.loading ? 'Refreshing…' : 'Change'}
@@ -949,6 +1158,17 @@ const FeedbackModal = () => {
949
1158
  <react_native_1.Text style={styles.machineMeta}>{machineCard.detail}</react_native_1.Text>
950
1159
  </react_native_1.Pressable>
951
1160
 
1161
+ <react_native_1.View style={styles.machineRoutes}>
1162
+ <react_native_1.View style={styles.machineRouteCard}>
1163
+ <react_native_1.Text style={styles.machineRouteLabel}>Coding machine</react_native_1.Text>
1164
+ <react_native_1.Text style={styles.machineRouteValue} numberOfLines={1}>{machineRouting.codingDeviceId || machineCard.title}</react_native_1.Text>
1165
+ </react_native_1.View>
1166
+ <react_native_1.View style={styles.machineRouteCard}>
1167
+ <react_native_1.Text style={styles.machineRouteLabel}>Render machine</react_native_1.Text>
1168
+ <react_native_1.Text style={styles.machineRouteValue} numberOfLines={1}>{machineRouting.renderDeviceId || machineRouting.codingDeviceId || machineCard.title}</react_native_1.Text>
1169
+ </react_native_1.View>
1170
+ </react_native_1.View>
1171
+
952
1172
  <react_native_1.View style={styles.runnerSection}>
953
1173
  <react_native_1.View style={styles.runnerSectionHeader}>
954
1174
  <react_native_1.View style={{ flex: 1 }}>
@@ -969,6 +1189,13 @@ const FeedbackModal = () => {
969
1189
  </react_native_1.Pressable>
970
1190
  </react_native_1.View>
971
1191
 
1192
+ <react_native_1.View style={styles.routingSummary}>
1193
+ <react_native_1.Text style={styles.routingSummaryLabel}>Vibing uses</react_native_1.Text>
1194
+ <react_native_1.Text style={styles.routingSummaryValue} numberOfLines={1}>
1195
+ {[preferredRunner || 'automatic runner', preferredModel].filter(Boolean).join(' · ')}
1196
+ </react_native_1.Text>
1197
+ </react_native_1.View>
1198
+
972
1199
  {runnerCards.map((row) => (<react_native_1.View key={row.id} style={[
973
1200
  styles.runnerCard,
974
1201
  row.tone === 'ok' && styles.runnerCardOk,
@@ -987,13 +1214,29 @@ const FeedbackModal = () => {
987
1214
  {row.statusLine}
988
1215
  </react_native_1.Text>
989
1216
  </react_native_1.View>
990
- {row.actionRunner ? (<react_native_1.Pressable onPress={() => setRunnerAuthModal(row.actionRunner ?? null)} style={({ pressed }) => [
991
- styles.runnerActionBtn,
992
- pressed && styles.buttonPressed,
993
- ]} accessibilityRole="button" accessibilityLabel={`${row.actionLabel} ${row.name}`}>
994
- <react_native_1.Text style={styles.runnerActionBtnText}>{row.actionLabel}</react_native_1.Text>
995
- </react_native_1.Pressable>) : null}
1217
+ <react_native_1.View style={styles.runnerCardActions}>
1218
+ {row.ready ? (<react_native_1.Pressable onPress={() => {
1219
+ const nextModel = row.models?.find((model) => model.isDefault)?.id || row.models?.[0]?.id || '';
1220
+ setPreferredRunnerState(row.id);
1221
+ setPreferredModelState(nextModel);
1222
+ void (0, preferences_1.setPreferredRunner)(row.id);
1223
+ void (0, preferences_1.setPreferredModel)(nextModel || null);
1224
+ }} style={({ pressed }) => [styles.runnerActionBtn, preferredRunner === row.id && styles.runnerActionBtnSelected, pressed && styles.buttonPressed]} accessibilityRole="button" accessibilityLabel={`Use ${row.name} for Vibing`}>
1225
+ <react_native_1.Text style={styles.runnerActionBtnText}>{preferredRunner === row.id ? 'Using' : 'Use'}</react_native_1.Text>
1226
+ </react_native_1.Pressable>) : null}
1227
+ {row.actionRunner ? (<react_native_1.Pressable onPress={() => setRunnerAuthModal(row.actionRunner ?? null)} style={({ pressed }) => [styles.runnerActionBtn, pressed && styles.buttonPressed]} accessibilityRole="button" accessibilityLabel={`${row.actionLabel} ${row.name}`}>
1228
+ <react_native_1.Text style={styles.runnerActionBtnText}>{row.actionLabel}</react_native_1.Text>
1229
+ </react_native_1.Pressable>) : null}
1230
+ </react_native_1.View>
996
1231
  </react_native_1.View>
1232
+ {preferredRunner === row.id && (row.models?.length || 0) > 0 ? (<react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.modelChoiceRow}>
1233
+ {row.models.map((model) => (<react_native_1.Pressable key={model.id} onPress={() => {
1234
+ setPreferredModelState(model.id);
1235
+ void (0, preferences_1.setPreferredModel)(model.id);
1236
+ }} style={[styles.modelChoice, preferredModel === model.id && styles.modelChoiceSelected]} accessibilityRole="button" accessibilityLabel={`Use ${model.name || model.id} model`}>
1237
+ <react_native_1.Text style={[styles.modelChoiceText, preferredModel === model.id && styles.modelChoiceTextSelected]}>{model.name || model.id}</react_native_1.Text>
1238
+ </react_native_1.Pressable>))}
1239
+ </react_native_1.ScrollView>) : null}
997
1240
  {row.detail ? (<react_native_1.Text style={styles.runnerCardDetail}>{row.detail}</react_native_1.Text>) : null}
998
1241
  </react_native_1.View>))}
999
1242
 
@@ -1073,7 +1316,10 @@ const FeedbackModal = () => {
1073
1316
  : reloadAction.disabledReason}
1074
1317
  </react_native_1.Text>
1075
1318
  </react_native_1.View>))}
1319
+ </>
1320
+ </react_native_1.View>
1076
1321
 
1322
+ <react_native_1.View style={[styles.tabContent, activeTab !== 'chat' && styles.hidden]}>
1077
1323
  {/* 3. Vibing — expands to an input box on first tap
1078
1324
  so the user says WHAT they want to vibe on, just
1079
1325
  like the Yaver mobile app's Vibing tab. Second
@@ -1100,7 +1346,7 @@ const FeedbackModal = () => {
1100
1346
  </react_native_1.Text>)}
1101
1347
 
1102
1348
  {/* Screenshot & Fix */}
1103
- <ActionRow label={action === 'capturing' ? 'Working…' : 'Screenshot & Fix'} tint="#22c55e" onPress={handleScreenshotAndFix} disabled={busy} busy={action === 'capturing'}/>
1349
+ {!dogfoodActive ? <ActionRow label={action === 'capturing' ? 'Working…' : 'Screenshot & Fix'} tint="#22c55e" onPress={handleScreenshotAndFix} disabled={busy} busy={action === 'capturing'}/> : null}
1104
1350
 
1105
1351
  {/* Deploy — opens an inline panel that talks to
1106
1352
  /fleet/deploy-options on the agent and lets the user
@@ -1108,7 +1354,8 @@ const FeedbackModal = () => {
1108
1354
  it on. Capabilities (e.g. "Linux can't TestFlight")
1109
1355
  come from the agent's doctor probes — no client-side
1110
1356
  platform smarts here. */}
1111
- {!showDeploy ? (<ActionRow label="Deploy" tint="#7f8cf7" onPress={() => setShowDeploy(true)} disabled={busy}/>) : (<DeployPanel_1.DeployPanel onClose={() => setShowDeploy(false)}/>)}
1357
+ {!dogfoodActive && (!showDeploy ? (<ActionRow label="Deploy" tint="#7f8cf7" onPress={() => setShowDeploy(true)} disabled={busy}/>) : (<DeployPanel_1.DeployPanel onClose={() => setShowDeploy(false)}/>))}
1358
+ </react_native_1.View>
1112
1359
 
1113
1360
  {progress !== null && (<react_native_1.View style={styles.progressTrack}>
1114
1361
  <react_native_1.View style={[
@@ -1149,6 +1396,24 @@ const ActionRow = ({ label, tint, onPress, disabled, busy, }) => (<react_native_
1149
1396
  {busy ? (<react_native_1.ActivityIndicator color={tint} size="small"/>) : (<react_native_1.Text style={[styles.actionText, { color: tint }]}>{label}</react_native_1.Text>)}
1150
1397
  </react_native_1.Pressable>);
1151
1398
  const styles = react_native_1.StyleSheet.create({
1399
+ dogfoodWizard: { gap: 10, borderRadius: 14, borderWidth: 1, borderColor: 'rgba(129,140,248,0.38)', backgroundColor: 'rgba(129,140,248,0.08)', padding: 13 },
1400
+ dogfoodWizardTitle: { color: '#222229', fontSize: 17, fontWeight: '800' },
1401
+ dogfoodWizardHint: { color: '#6f6f7b', fontSize: 12, lineHeight: 17 },
1402
+ dogfoodInstallationId: { color: '#6555df', fontSize: 11, fontFamily: react_native_1.Platform.select({ ios: 'Menlo', android: 'monospace', default: 'monospace' }) },
1403
+ dogfoodPendingBox: { gap: 9, borderRadius: 11, padding: 10, backgroundColor: 'rgba(245,158,11,0.10)' },
1404
+ dogfoodPendingText: { color: '#8a5a10', fontSize: 12, lineHeight: 17 },
1405
+ dogfoodStepLabel: { color: '#555561', fontSize: 11, fontWeight: '800', textTransform: 'uppercase', letterSpacing: 0.7 },
1406
+ dogfoodChoiceRow: { flexDirection: 'row', gap: 7 },
1407
+ dogfoodChoice: { borderRadius: 9, borderWidth: 1, borderColor: '#d8d8e3', backgroundColor: '#fff', paddingHorizontal: 10, paddingVertical: 8 },
1408
+ dogfoodChoiceSelected: { borderColor: '#818cf8', backgroundColor: 'rgba(129,140,248,0.18)' },
1409
+ dogfoodChoiceText: { color: '#666671', fontSize: 12, fontWeight: '700' },
1410
+ dogfoodChoiceTextSelected: { color: '#5645d8' },
1411
+ dogfoodConsole: { maxHeight: 260, gap: 4, borderRadius: 11, padding: 10, backgroundColor: '#15151b' },
1412
+ dogfoodConsoleStatus: { color: '#a5b4fc', fontSize: 12, fontWeight: '800' },
1413
+ dogfoodConsoleLine: { color: '#d1d5db', fontSize: 10, lineHeight: 14, fontFamily: react_native_1.Platform.select({ ios: 'Menlo', android: 'monospace', default: 'monospace' }) },
1414
+ dogfoodConsoleError: { color: '#fca5a5', fontSize: 11, lineHeight: 16, marginTop: 5 },
1415
+ dogfoodOpenPreview: { alignSelf: 'flex-start', borderRadius: 9, paddingHorizontal: 11, paddingVertical: 8, marginTop: 6, backgroundColor: '#6555df' },
1416
+ dogfoodOpenPreviewText: { color: '#fff', fontSize: 12, fontWeight: '800' },
1152
1417
  reloadRow: {
1153
1418
  gap: 4,
1154
1419
  },
@@ -1167,7 +1432,7 @@ const styles = react_native_1.StyleSheet.create({
1167
1432
  gap: 10,
1168
1433
  },
1169
1434
  vibeInput: {
1170
- color: '#fff',
1435
+ color: '#24242b',
1171
1436
  fontSize: 15,
1172
1437
  minHeight: 64,
1173
1438
  textAlignVertical: 'top',
@@ -1218,7 +1483,7 @@ const styles = react_native_1.StyleSheet.create({
1218
1483
  justifyContent: 'flex-end',
1219
1484
  },
1220
1485
  modal: {
1221
- backgroundColor: '#141422',
1486
+ backgroundColor: '#f8f8fb',
1222
1487
  borderTopLeftRadius: 22,
1223
1488
  borderTopRightRadius: 22,
1224
1489
  padding: 22,
@@ -1242,7 +1507,7 @@ const styles = react_native_1.StyleSheet.create({
1242
1507
  title: {
1243
1508
  fontSize: 20,
1244
1509
  fontWeight: '700',
1245
- color: '#fff',
1510
+ color: '#17171d',
1246
1511
  },
1247
1512
  closeBtn: {
1248
1513
  width: 36,
@@ -1250,10 +1515,10 @@ const styles = react_native_1.StyleSheet.create({
1250
1515
  borderRadius: 18,
1251
1516
  alignItems: 'center',
1252
1517
  justifyContent: 'center',
1253
- backgroundColor: 'rgba(255,255,255,0.08)',
1518
+ backgroundColor: '#ededf3',
1254
1519
  },
1255
1520
  closeIcon: {
1256
- color: '#fff',
1521
+ color: '#656570',
1257
1522
  fontSize: 22,
1258
1523
  lineHeight: 24,
1259
1524
  fontWeight: '400',
@@ -1272,12 +1537,19 @@ const styles = react_native_1.StyleSheet.create({
1272
1537
  fontSize: 15,
1273
1538
  fontWeight: '700',
1274
1539
  },
1540
+ tabs: { flexDirection: 'row', gap: 6, padding: 3, borderRadius: 12, backgroundColor: '#ededf3' },
1541
+ tab: { flex: 1, minHeight: 36, borderRadius: 9, alignItems: 'center', justifyContent: 'center' },
1542
+ tabSelected: { backgroundColor: '#fff' },
1543
+ tabText: { color: '#858590', fontSize: 12, fontWeight: '700' },
1544
+ tabTextSelected: { color: '#6252e8' },
1545
+ tabContent: { gap: 12 },
1546
+ hidden: { display: 'none' },
1275
1547
  machineCard: {
1276
1548
  borderRadius: 14,
1277
1549
  borderWidth: 1,
1278
1550
  padding: 14,
1279
- backgroundColor: 'rgba(255,255,255,0.04)',
1280
- borderColor: 'rgba(255,255,255,0.12)',
1551
+ backgroundColor: '#fff',
1552
+ borderColor: '#e1e1e8',
1281
1553
  },
1282
1554
  machineCardLive: {
1283
1555
  backgroundColor: 'rgba(34,197,94,0.10)',
@@ -1291,6 +1563,10 @@ const styles = react_native_1.StyleSheet.create({
1291
1563
  backgroundColor: 'rgba(239,68,68,0.10)',
1292
1564
  borderColor: 'rgba(239,68,68,0.35)',
1293
1565
  },
1566
+ machineRoutes: { flexDirection: 'row', gap: 8 },
1567
+ machineRouteCard: { flex: 1, minWidth: 0, borderRadius: 11, padding: 10, gap: 3, backgroundColor: '#fff', borderWidth: 1, borderColor: '#e1e1e8' },
1568
+ machineRouteLabel: { color: '#7b7b86', fontSize: 10, fontWeight: '700', textTransform: 'uppercase' },
1569
+ machineRouteValue: { color: '#222229', fontSize: 12, fontWeight: '700' },
1294
1570
  machineHeader: {
1295
1571
  flexDirection: 'row',
1296
1572
  alignItems: 'center',
@@ -1318,7 +1594,7 @@ const styles = react_native_1.StyleSheet.create({
1318
1594
  backgroundColor: '#ef4444',
1319
1595
  },
1320
1596
  machineLabel: {
1321
- color: '#cbd5e1',
1597
+ color: '#73737e',
1322
1598
  fontSize: 12,
1323
1599
  fontWeight: '700',
1324
1600
  textTransform: 'uppercase',
@@ -1330,12 +1606,12 @@ const styles = react_native_1.StyleSheet.create({
1330
1606
  fontWeight: '700',
1331
1607
  },
1332
1608
  machineName: {
1333
- color: '#fff',
1609
+ color: '#222229',
1334
1610
  fontSize: 16,
1335
1611
  fontWeight: '700',
1336
1612
  },
1337
1613
  machineMeta: {
1338
- color: '#cbd5e1',
1614
+ color: '#73737e',
1339
1615
  fontSize: 12,
1340
1616
  marginTop: 4,
1341
1617
  lineHeight: 17,
@@ -1350,33 +1626,33 @@ const styles = react_native_1.StyleSheet.create({
1350
1626
  gap: 10,
1351
1627
  },
1352
1628
  runnerSectionTitle: {
1353
- color: '#f8fafc',
1629
+ color: '#222229',
1354
1630
  fontSize: 16,
1355
1631
  fontWeight: '700',
1356
1632
  },
1357
1633
  runnerSectionSubtitle: {
1358
1634
  marginTop: 2,
1359
- color: '#94a3b8',
1635
+ color: '#83838e',
1360
1636
  fontSize: 12,
1361
1637
  },
1362
1638
  runnerRefreshBtn: {
1363
1639
  borderRadius: 10,
1364
1640
  borderWidth: 1,
1365
- borderColor: 'rgba(148,163,184,0.22)',
1366
- backgroundColor: 'rgba(15,23,42,0.65)',
1641
+ borderColor: '#dedee7',
1642
+ backgroundColor: '#fff',
1367
1643
  paddingHorizontal: 10,
1368
1644
  paddingVertical: 8,
1369
1645
  },
1370
1646
  runnerRefreshBtnText: {
1371
- color: '#cbd5e1',
1647
+ color: '#5f5f69',
1372
1648
  fontSize: 12,
1373
1649
  fontWeight: '600',
1374
1650
  },
1375
1651
  runnerCard: {
1376
1652
  borderRadius: 12,
1377
1653
  borderWidth: 1,
1378
- borderColor: 'rgba(148,163,184,0.14)',
1379
- backgroundColor: 'rgba(15,23,42,0.45)',
1654
+ borderColor: '#e2e2e9',
1655
+ backgroundColor: '#fff',
1380
1656
  paddingHorizontal: 12,
1381
1657
  paddingVertical: 11,
1382
1658
  gap: 6,
@@ -1398,15 +1674,16 @@ const styles = react_native_1.StyleSheet.create({
1398
1674
  alignItems: 'center',
1399
1675
  gap: 10,
1400
1676
  },
1677
+ runnerCardActions: { flexDirection: 'row', alignItems: 'center', gap: 6 },
1401
1678
  runnerCardTitle: {
1402
- color: '#f8fafc',
1679
+ color: '#222229',
1403
1680
  fontSize: 14,
1404
1681
  fontWeight: '700',
1405
1682
  },
1406
1683
  runnerCardStatus: {
1407
1684
  marginTop: 2,
1408
1685
  fontSize: 12,
1409
- color: '#cbd5e1',
1686
+ color: '#666671',
1410
1687
  },
1411
1688
  runnerCardStatusOk: {
1412
1689
  color: '#86efac',
@@ -1418,7 +1695,7 @@ const styles = react_native_1.StyleSheet.create({
1418
1695
  color: '#fca5a5',
1419
1696
  },
1420
1697
  runnerCardDetail: {
1421
- color: '#94a3b8',
1698
+ color: '#858590',
1422
1699
  fontSize: 11,
1423
1700
  lineHeight: 16,
1424
1701
  },
@@ -1435,6 +1712,15 @@ const styles = react_native_1.StyleSheet.create({
1435
1712
  fontSize: 12,
1436
1713
  fontWeight: '700',
1437
1714
  },
1715
+ runnerActionBtnSelected: { borderColor: '#818cf8', backgroundColor: 'rgba(79,70,229,0.44)' },
1716
+ routingSummary: { flexDirection: 'row', alignItems: 'center', gap: 8, paddingHorizontal: 2 },
1717
+ routingSummaryLabel: { color: '#7c7c87', fontSize: 11, fontWeight: '600' },
1718
+ routingSummaryValue: { flex: 1, color: '#6555df', fontSize: 12, fontWeight: '700', textAlign: 'right' },
1719
+ modelChoiceRow: { gap: 6, paddingTop: 2 },
1720
+ modelChoice: { borderRadius: 9, borderWidth: 1, borderColor: 'rgba(148,163,184,0.18)', paddingHorizontal: 9, paddingVertical: 6 },
1721
+ modelChoiceSelected: { borderColor: '#818cf8', backgroundColor: 'rgba(79,70,229,0.30)' },
1722
+ modelChoiceText: { color: '#73737e', fontSize: 11 },
1723
+ modelChoiceTextSelected: { color: '#5e4ce6', fontWeight: '700' },
1438
1724
  runnerSectionError: {
1439
1725
  color: '#fca5a5',
1440
1726
  fontSize: 12,
@@ -1446,7 +1732,7 @@ const styles = react_native_1.StyleSheet.create({
1446
1732
  progressTrack: {
1447
1733
  height: 6,
1448
1734
  borderRadius: 3,
1449
- backgroundColor: 'rgba(255,255,255,0.08)',
1735
+ backgroundColor: '#e5e5eb',
1450
1736
  overflow: 'hidden',
1451
1737
  marginTop: 4,
1452
1738
  },
@@ -1473,10 +1759,10 @@ const styles = react_native_1.StyleSheet.create({
1473
1759
  paddingVertical: 6,
1474
1760
  paddingHorizontal: 10,
1475
1761
  borderRadius: 10,
1476
- backgroundColor: 'rgba(255,255,255,0.05)',
1762
+ backgroundColor: '#ededf3',
1477
1763
  },
1478
1764
  quickIconToggleText: {
1479
- color: '#cbd5e1',
1765
+ color: '#5f5f69',
1480
1766
  fontSize: 12,
1481
1767
  fontWeight: '700',
1482
1768
  },
@@ -1488,25 +1774,25 @@ const styles = react_native_1.StyleSheet.create({
1488
1774
  padding: 12,
1489
1775
  },
1490
1776
  quickIconNoteText: {
1491
- color: '#fde68a',
1777
+ color: '#8a5a12',
1492
1778
  fontSize: 12,
1493
1779
  lineHeight: 17,
1494
1780
  },
1495
1781
  iconSelector: {
1496
1782
  borderRadius: 12,
1497
1783
  borderWidth: 1,
1498
- borderColor: 'rgba(255,255,255,0.09)',
1499
- backgroundColor: 'rgba(255,255,255,0.03)',
1784
+ borderColor: '#e1e1e8',
1785
+ backgroundColor: '#fff',
1500
1786
  padding: 12,
1501
1787
  gap: 10,
1502
1788
  },
1503
1789
  iconSelectorTitle: {
1504
- color: '#f8fafc',
1790
+ color: '#222229',
1505
1791
  fontSize: 13,
1506
1792
  fontWeight: '700',
1507
1793
  },
1508
1794
  iconSelectorText: {
1509
- color: '#cbd5e1',
1795
+ color: '#73737e',
1510
1796
  fontSize: 12,
1511
1797
  lineHeight: 17,
1512
1798
  },
@@ -1520,8 +1806,8 @@ const styles = react_native_1.StyleSheet.create({
1520
1806
  minWidth: 84,
1521
1807
  borderRadius: 12,
1522
1808
  borderWidth: 1,
1523
- borderColor: 'rgba(255,255,255,0.08)',
1524
- backgroundColor: 'rgba(255,255,255,0.02)',
1809
+ borderColor: '#e4e4ea',
1810
+ backgroundColor: '#f8f8fb',
1525
1811
  paddingVertical: 10,
1526
1812
  paddingHorizontal: 8,
1527
1813
  alignItems: 'center',
@@ -1548,7 +1834,7 @@ const styles = react_native_1.StyleSheet.create({
1548
1834
  fontWeight: '700',
1549
1835
  },
1550
1836
  iconOptionText: {
1551
- color: '#e2e8f0',
1837
+ color: '#555561',
1552
1838
  fontSize: 11,
1553
1839
  fontWeight: '600',
1554
1840
  },
@@ -1556,14 +1842,14 @@ const styles = react_native_1.StyleSheet.create({
1556
1842
  marginTop: 4,
1557
1843
  borderRadius: 14,
1558
1844
  borderWidth: 1,
1559
- borderColor: 'rgba(255,255,255,0.1)',
1845
+ borderColor: '#e1e1e8',
1560
1846
  paddingVertical: 15,
1561
1847
  alignItems: 'center',
1562
1848
  justifyContent: 'center',
1563
- backgroundColor: 'rgba(255,255,255,0.04)',
1849
+ backgroundColor: '#fff',
1564
1850
  },
1565
1851
  cancelBtnText: {
1566
- color: '#e5e7eb',
1852
+ color: '#555561',
1567
1853
  fontSize: 15,
1568
1854
  fontWeight: '700',
1569
1855
  },