yaver-feedback-react-native 0.9.9 → 0.9.11
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 +32 -1
- package/dist/DogfoodRuntime.js +94 -14
- package/dist/DogfoodSessionUi.d.ts +2 -0
- package/dist/DogfoodSessionUi.js +8 -4
- package/dist/FeedbackModal.js +263 -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 +59 -0
- package/dist/__tests__/FeedbackModalContract.test.d.ts +1 -0
- package/dist/__tests__/FeedbackModalContract.test.js +77 -0
- package/dist/__tests__/NativeDogfoodShortcut.test.js +23 -0
- package/dist/__tests__/P2PDogfoodDriver.test.js +4 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.js +7 -7
- package/ios/YaverHotReload.swift +3 -4
- package/package.json +1 -1
- package/src/DogfoodRuntime.ts +129 -15
- package/src/DogfoodSessionUi.tsx +10 -4
- package/src/FeedbackModal.tsx +335 -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 +66 -0
- package/src/__tests__/FeedbackModalContract.test.ts +88 -0
- package/src/__tests__/NativeDogfoodShortcut.test.ts +25 -0
- package/src/__tests__/P2PDogfoodDriver.test.ts +4 -0
- package/src/index.ts +6 -5
package/app.plugin.js
CHANGED
|
@@ -227,7 +227,9 @@ function withYaverAppDelegateHook(config) {
|
|
|
227
227
|
// Existing consumers may already have the hot-reload hook from an older
|
|
228
228
|
// SDK. Still apply newer, independently-versioned native contracts.
|
|
229
229
|
if (contents.includes("YaverHotReload")) {
|
|
230
|
-
config.modResults.contents = patchDogfoodAppShortcut(
|
|
230
|
+
config.modResults.contents = patchDogfoodAppShortcut(
|
|
231
|
+
patchHotReloadBootConfirmation(contents)
|
|
232
|
+
);
|
|
231
233
|
return config;
|
|
232
234
|
}
|
|
233
235
|
|
|
@@ -273,20 +275,14 @@ function withYaverAppDelegateHook(config) {
|
|
|
273
275
|
name: Notification.Name("YaverHotReloadBundle"),
|
|
274
276
|
object: nil
|
|
275
277
|
)
|
|
276
|
-
// Crash-revert safety net:
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
// YaverHotReload.bundleURL() will eventually revert to the
|
|
280
|
-
// TestFlight-installed bundle after 3 failed boots. See
|
|
281
|
-
// YaverHotReload.swift for the full state machine.
|
|
278
|
+
// Crash-revert safety net: only a real first React frame confirms boot.
|
|
279
|
+
// Uptime is not success: JS can fail while the native process remains
|
|
280
|
+
// alive on its launch screen indefinitely.
|
|
282
281
|
NotificationCenter.default.addObserver(
|
|
283
282
|
forName: NSNotification.Name(rawValue: "RCTContentDidAppearNotification"),
|
|
284
283
|
object: nil,
|
|
285
284
|
queue: .main
|
|
286
285
|
) { _ in YaverHotReload.markBootSuccessful() }
|
|
287
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
|
|
288
|
-
YaverHotReload.markBootSuccessful()
|
|
289
|
-
}
|
|
290
286
|
}
|
|
291
287
|
|
|
292
288
|
@objc private func yaverHandleHotReload(_ notification: Notification) {
|
|
@@ -374,6 +370,23 @@ function withYaverAppDelegateHook(config) {
|
|
|
374
370
|
});
|
|
375
371
|
}
|
|
376
372
|
|
|
373
|
+
/** Upgrade an already-generated AppDelegate from the pre-0.9.10 boot guard.
|
|
374
|
+
* Older plugins treated ten seconds of process uptime as a successful React
|
|
375
|
+
* boot. A JS exception can leave that process alive on the native splash, so
|
|
376
|
+
* the timer reset the crash counter forever and prevented bundle rollback. */
|
|
377
|
+
function patchHotReloadBootConfirmation(contents) {
|
|
378
|
+
let patched = contents;
|
|
379
|
+
patched = patched.replace(
|
|
380
|
+
/ \/\/ Crash-revert safety net: clear the boot-attempt counter once\n \/\/ RN renders its first frame, OR after 10 s of uptime — whichever\n \/\/ fires first\. If neither fires \(bundle crashes before render\),\n \/\/ YaverHotReload\.bundleURL\(\) will eventually revert to the\n \/\/ TestFlight-installed bundle after 3 failed boots\. See\n \/\/ YaverHotReload\.swift for the full state machine\./,
|
|
381
|
+
` // Crash-revert safety net: only a real first React frame confirms boot.\n // Uptime is not success: JS can fail while the native process remains\n // alive on its launch screen indefinitely.`
|
|
382
|
+
);
|
|
383
|
+
patched = patched.replace(
|
|
384
|
+
/ DispatchQueue\.main\.asyncAfter\(deadline: \.now\(\) \+ 10\) \{\n YaverHotReload\.markBootSuccessful\(\)\n \}\n?/g,
|
|
385
|
+
""
|
|
386
|
+
);
|
|
387
|
+
return patched;
|
|
388
|
+
}
|
|
389
|
+
|
|
377
390
|
/** Wire the dynamic iOS Home Screen shortcut into the SDK native module.
|
|
378
391
|
* The shortcut itself is created at runtime only after backend ACL success;
|
|
379
392
|
* this hook merely consumes a user-selected action on warm/cold launch. */
|
|
@@ -820,6 +833,7 @@ const yaverFeedbackPlugin = createRunOncePlugin(
|
|
|
820
833
|
// config-plugin seam catches template drift before a consumer discovers it in
|
|
821
834
|
// an archive build.
|
|
822
835
|
yaverFeedbackPlugin.__test = {
|
|
836
|
+
patchHotReloadBootConfirmation,
|
|
823
837
|
patchDogfoodAppShortcut,
|
|
824
838
|
patchDogfoodSceneDelegate,
|
|
825
839
|
findAppDelegateClassClose,
|
package/dist/DogfoodRuntime.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export interface DogfoodProject {
|
|
|
17
17
|
workDir: string;
|
|
18
18
|
framework: string;
|
|
19
19
|
lane: DogfoodLane;
|
|
20
|
+
/**
|
|
21
|
+
* Optional automatic recovery lane. The shared onboarding flow uses the
|
|
22
|
+
* browser lane here when a user prefers Hermes or WebRTC for a project that
|
|
23
|
+
* can also render in a browser. The failed preferred attempt remains in the
|
|
24
|
+
* live console; the fallback is never silent.
|
|
25
|
+
*/
|
|
26
|
+
fallbackLane?: DogfoodLane;
|
|
20
27
|
/** Optional source URL for drivers that can clone missing source. */
|
|
21
28
|
repositoryUrl?: string;
|
|
22
29
|
/** Optional native target from /remote-runtime/capabilities for WebRTC. */
|
|
@@ -29,12 +36,36 @@ export interface DogfoodLaneOption {
|
|
|
29
36
|
default: boolean;
|
|
30
37
|
reason?: string;
|
|
31
38
|
}
|
|
39
|
+
export interface DogfoodLanePlan {
|
|
40
|
+
preferred: DogfoodLane;
|
|
41
|
+
/** Browser is the cheap, portable recovery lane for RN/Expo/Flutter/web. */
|
|
42
|
+
fallback?: DogfoodLane;
|
|
43
|
+
options: DogfoodLaneOption[];
|
|
44
|
+
}
|
|
32
45
|
/** One framework-to-lane matrix for Yaver and third-party consumers. */
|
|
33
46
|
export declare function dogfoodLaneOptions(framework: string, capabilities?: {
|
|
34
47
|
nativeRuntimeAvailable?: boolean;
|
|
48
|
+
browserRuntimeAvailable?: boolean;
|
|
35
49
|
selfDevelopment?: boolean;
|
|
36
50
|
}): DogfoodLaneOption[];
|
|
37
|
-
export declare function defaultDogfoodLane(framework: string
|
|
51
|
+
export declare function defaultDogfoodLane(framework: string, capabilities?: {
|
|
52
|
+
nativeRuntimeAvailable?: boolean;
|
|
53
|
+
browserRuntimeAvailable?: boolean;
|
|
54
|
+
selfDevelopment?: boolean;
|
|
55
|
+
}): DogfoodLane;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve one ordered lane policy for Yaver and embedded SDK hosts.
|
|
58
|
+
*
|
|
59
|
+
* Browser is the onboarding default for browser-capable React Native, Expo,
|
|
60
|
+
* Flutter, and web projects. When the user explicitly prefers Hermes or
|
|
61
|
+
* WebRTC, browser becomes the automatic second attempt. Native-only projects
|
|
62
|
+
* remain WebRTC-only and never advertise a browser recovery they cannot run.
|
|
63
|
+
*/
|
|
64
|
+
export declare function dogfoodLanePlan(framework: string, capabilities?: {
|
|
65
|
+
nativeRuntimeAvailable?: boolean;
|
|
66
|
+
browserRuntimeAvailable?: boolean;
|
|
67
|
+
selfDevelopment?: boolean;
|
|
68
|
+
}, preferred?: DogfoodLane | null): DogfoodLanePlan;
|
|
38
69
|
export interface DogfoodLogLine {
|
|
39
70
|
text: string;
|
|
40
71
|
at: number;
|
package/dist/DogfoodRuntime.js
CHANGED
|
@@ -15,13 +15,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.DogfoodController = exports.dogfoodLogLinesFromDevEvent = exports.DogfoodRuntimeError = void 0;
|
|
16
16
|
exports.dogfoodLaneOptions = dogfoodLaneOptions;
|
|
17
17
|
exports.defaultDogfoodLane = defaultDogfoodLane;
|
|
18
|
+
exports.dogfoodLanePlan = dogfoodLanePlan;
|
|
18
19
|
exports.validateDogfoodProject = validateDogfoodProject;
|
|
19
20
|
exports.runtimeLogLinesFromDevEvent = runtimeLogLinesFromDevEvent;
|
|
20
21
|
/** One framework-to-lane matrix for Yaver and third-party consumers. */
|
|
21
22
|
function dogfoodLaneOptions(framework, capabilities = {}) {
|
|
22
23
|
const normalized = String(framework || '').trim().toLowerCase();
|
|
23
24
|
const reactNative = normalized === 'expo' || normalized === 'react-native';
|
|
24
|
-
|
|
25
|
+
// The framework matrix is the safe default. A positively detected browser
|
|
26
|
+
// target may add an exception (for example SwiftWasm/Tokamak), but a missing
|
|
27
|
+
// browser binary must not incorrectly remove RN/Flutter's browser build lane.
|
|
28
|
+
const browserCapable = capabilities.browserRuntimeAvailable === true || reactNative || [
|
|
25
29
|
'flutter', 'web', 'next', 'nextjs', 'vite', 'remix', 'svelte', 'vue', 'angular',
|
|
26
30
|
].includes(normalized);
|
|
27
31
|
const nativeAvailable = capabilities.nativeRuntimeAvailable === true;
|
|
@@ -40,8 +44,31 @@ function dogfoodLaneOptions(framework, capabilities = {}) {
|
|
|
40
44
|
},
|
|
41
45
|
];
|
|
42
46
|
}
|
|
43
|
-
function defaultDogfoodLane(framework) {
|
|
44
|
-
|
|
47
|
+
function defaultDogfoodLane(framework, capabilities = {}) {
|
|
48
|
+
const options = dogfoodLaneOptions(framework, capabilities);
|
|
49
|
+
return options.find((option) => option.default && option.supported)?.lane
|
|
50
|
+
|| options.find((option) => option.supported)?.lane
|
|
51
|
+
|| 'browser';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve one ordered lane policy for Yaver and embedded SDK hosts.
|
|
55
|
+
*
|
|
56
|
+
* Browser is the onboarding default for browser-capable React Native, Expo,
|
|
57
|
+
* Flutter, and web projects. When the user explicitly prefers Hermes or
|
|
58
|
+
* WebRTC, browser becomes the automatic second attempt. Native-only projects
|
|
59
|
+
* remain WebRTC-only and never advertise a browser recovery they cannot run.
|
|
60
|
+
*/
|
|
61
|
+
function dogfoodLanePlan(framework, capabilities = {}, preferred) {
|
|
62
|
+
const options = dogfoodLaneOptions(framework, capabilities);
|
|
63
|
+
const supported = (lane) => !!lane && options.some((option) => option.lane === lane && option.supported);
|
|
64
|
+
const resolved = supported(preferred)
|
|
65
|
+
? preferred
|
|
66
|
+
: defaultDogfoodLane(framework, capabilities);
|
|
67
|
+
return {
|
|
68
|
+
preferred: resolved,
|
|
69
|
+
fallback: resolved !== 'browser' && supported('browser') ? 'browser' : undefined,
|
|
70
|
+
options,
|
|
71
|
+
};
|
|
45
72
|
}
|
|
46
73
|
class DogfoodRuntimeError extends Error {
|
|
47
74
|
constructor(failure) {
|
|
@@ -167,8 +194,8 @@ class DogfoodController {
|
|
|
167
194
|
phase: 'preparing', attempt, project: this.project,
|
|
168
195
|
message: `Preparing ${this.project.name}…`, logs: [], startedAt: Date.now(),
|
|
169
196
|
});
|
|
170
|
-
const
|
|
171
|
-
project
|
|
197
|
+
const makeContext = (project) => ({
|
|
198
|
+
project,
|
|
172
199
|
attempt,
|
|
173
200
|
log: (line) => {
|
|
174
201
|
if (generation !== this.generation)
|
|
@@ -195,16 +222,69 @@ class DogfoodController {
|
|
|
195
222
|
this.cleanups.set(generation, owned);
|
|
196
223
|
},
|
|
197
224
|
isCurrent: () => generation === this.generation,
|
|
198
|
-
};
|
|
225
|
+
});
|
|
226
|
+
let activeProject = this.project;
|
|
227
|
+
let context = makeContext(activeProject);
|
|
199
228
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
229
|
+
const start = async () => {
|
|
230
|
+
await this.driver.prepare?.(context);
|
|
231
|
+
if (!context.isCurrent())
|
|
232
|
+
throw new DogfoodRuntimeError({
|
|
233
|
+
code: 'DOGFOOD_ATTEMPT_REPLACED', error: 'A newer Dogfood attempt replaced this one.',
|
|
234
|
+
remedy: 'Wait for the newer attempt.', retryable: true,
|
|
235
|
+
});
|
|
236
|
+
context.setPhase('starting', `Starting ${activeProject.name} on the ${activeProject.lane} lane…`);
|
|
237
|
+
return this.driver.start(context);
|
|
238
|
+
};
|
|
239
|
+
let result;
|
|
240
|
+
try {
|
|
241
|
+
result = await start();
|
|
242
|
+
}
|
|
243
|
+
catch (primaryError) {
|
|
244
|
+
const primaryFailure = failureFrom(primaryError);
|
|
245
|
+
const fallbackLane = activeProject.fallbackLane;
|
|
246
|
+
const mayFallback = !!fallbackLane
|
|
247
|
+
&& fallbackLane !== activeProject.lane
|
|
248
|
+
&& primaryFailure.retryable
|
|
249
|
+
&& primaryFailure.code !== 'DOGFOOD_ATTEMPT_REPLACED';
|
|
250
|
+
if (!mayFallback)
|
|
251
|
+
throw primaryError;
|
|
252
|
+
await this.runCleanups('all', generation);
|
|
253
|
+
const fallbackProject = {
|
|
254
|
+
...activeProject,
|
|
255
|
+
lane: fallbackLane,
|
|
256
|
+
fallbackLane: undefined,
|
|
257
|
+
};
|
|
258
|
+
const fallbackInvalid = validateDogfoodProject(fallbackProject);
|
|
259
|
+
if (fallbackInvalid)
|
|
260
|
+
throw primaryError;
|
|
261
|
+
const fallbackLine = {
|
|
262
|
+
text: `[fallback] ${activeProject.lane} failed (${primaryFailure.code}); trying ${fallbackLane}`,
|
|
263
|
+
at: Date.now(),
|
|
264
|
+
stream: 'system',
|
|
265
|
+
};
|
|
266
|
+
activeProject = fallbackProject;
|
|
267
|
+
this.replace({
|
|
268
|
+
...this.state,
|
|
269
|
+
project: activeProject,
|
|
270
|
+
phase: 'preparing',
|
|
271
|
+
message: `Recovering ${activeProject.name} in the ${fallbackLane} lane…`,
|
|
272
|
+
logs: [...this.state.logs, fallbackLine].slice(-this.maxLogLines),
|
|
273
|
+
lastOutputAt: fallbackLine.at,
|
|
274
|
+
failure: undefined,
|
|
205
275
|
});
|
|
206
|
-
|
|
207
|
-
|
|
276
|
+
context = makeContext(activeProject);
|
|
277
|
+
result = await start();
|
|
278
|
+
result = {
|
|
279
|
+
...result,
|
|
280
|
+
metadata: {
|
|
281
|
+
...result.metadata,
|
|
282
|
+
preferredLane: this.project.lane,
|
|
283
|
+
fallbackFrom: this.project.lane,
|
|
284
|
+
fallbackReason: primaryFailure.code,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
208
288
|
if (!context.isCurrent()) {
|
|
209
289
|
await this.runCleanups('all', generation);
|
|
210
290
|
throw new DogfoodRuntimeError({
|
|
@@ -212,7 +292,7 @@ class DogfoodController {
|
|
|
212
292
|
remedy: 'Wait for the newer attempt.', retryable: true,
|
|
213
293
|
});
|
|
214
294
|
}
|
|
215
|
-
this.replace({ ...this.state, phase: 'ready', message: `${
|
|
295
|
+
this.replace({ ...this.state, project: activeProject, phase: 'ready', message: `${activeProject.name} is ready`, result, failure: undefined });
|
|
216
296
|
return result;
|
|
217
297
|
}
|
|
218
298
|
catch (error) {
|
|
@@ -32,6 +32,7 @@ export declare const DogfoodStatusRail: React.FC<{
|
|
|
32
32
|
export declare const DogfoodLanePicker: React.FC<{
|
|
33
33
|
options: readonly DogfoodLaneOption[];
|
|
34
34
|
selected: DogfoodLane;
|
|
35
|
+
fallbackLane?: DogfoodLane;
|
|
35
36
|
onSelect: (lane: DogfoodLane) => void;
|
|
36
37
|
colors?: Partial<DogfoodUiColors>;
|
|
37
38
|
showUnsupportedReasons?: boolean;
|
|
@@ -40,6 +41,7 @@ export declare const DogfoodLanePicker: React.FC<{
|
|
|
40
41
|
* Logs; Hermes/WebRTC use the same lifecycle and failure/remedy treatment. */
|
|
41
42
|
export declare const DogfoodLiveConsole: React.FC<{
|
|
42
43
|
lane: DogfoodLane;
|
|
44
|
+
sourceLabel?: string;
|
|
43
45
|
phase: DogfoodPhase;
|
|
44
46
|
message: string;
|
|
45
47
|
logs: readonly DogfoodLogLine[];
|
package/dist/DogfoodSessionUi.js
CHANGED
|
@@ -54,7 +54,7 @@ const DogfoodStatusRail = ({ steps, colors: colorOverrides }) => {
|
|
|
54
54
|
};
|
|
55
55
|
exports.DogfoodStatusRail = DogfoodStatusRail;
|
|
56
56
|
/** One lane selector and one default policy across Yaver, SFMG, and Talos. */
|
|
57
|
-
const DogfoodLanePicker = ({ options, selected, onSelect, colors: colorOverrides, showUnsupportedReasons = true }) => {
|
|
57
|
+
const DogfoodLanePicker = ({ options, selected, fallbackLane, onSelect, colors: colorOverrides, showUnsupportedReasons = true }) => {
|
|
58
58
|
const colors = resolvedColors(colorOverrides);
|
|
59
59
|
return (<react_native_1.View accessibilityRole="radiogroup" accessibilityLabel="Dogfood runtime lane">
|
|
60
60
|
<react_native_1.View style={styles.choiceRow}>
|
|
@@ -69,7 +69,8 @@ const DogfoodLanePicker = ({ options, selected, onSelect, colors: colorOverrides
|
|
|
69
69
|
},
|
|
70
70
|
]}>
|
|
71
71
|
<react_native_1.Text style={[styles.choiceText, { color: colors.text }, active && styles.choiceTextActive]}>
|
|
72
|
-
{option.label}
|
|
72
|
+
{option.label}
|
|
73
|
+
{active ? ' · preferred' : fallbackLane === option.lane ? ' · automatic fallback' : option.default ? ' · default' : ''}
|
|
73
74
|
</react_native_1.Text>
|
|
74
75
|
</react_native_1.Pressable>);
|
|
75
76
|
})}
|
|
@@ -91,15 +92,17 @@ function runtimeTone(phase, colors) {
|
|
|
91
92
|
}
|
|
92
93
|
/** Shared second-stage live console. Browser lane deliberately names Browser
|
|
93
94
|
* Logs; Hermes/WebRTC use the same lifecycle and failure/remedy treatment. */
|
|
94
|
-
const DogfoodLiveConsole = ({ lane, phase, message, logs, failure, maxLines = 80, colors: colorOverrides, renderText }) => {
|
|
95
|
+
const DogfoodLiveConsole = ({ lane, sourceLabel, phase, message, logs, failure, maxLines = 80, colors: colorOverrides, renderText }) => {
|
|
95
96
|
const colors = resolvedColors(colorOverrides);
|
|
96
97
|
const text = logs.slice(-maxLines).map((line) => line.text).join('\n');
|
|
97
98
|
const title = lane === 'browser' ? 'Browser Logs' : lane === 'hermes' ? 'Hermes Logs' : 'WebRTC Logs';
|
|
98
|
-
|
|
99
|
+
const accessibleTitle = sourceLabel ? `${title} · ${sourceLabel}` : title;
|
|
100
|
+
return (<react_native_1.View style={[styles.console, { backgroundColor: colors.console, borderColor: colors.border }]} accessibilityLabel={accessibleTitle}>
|
|
99
101
|
<react_native_1.View style={styles.consoleHeader}>
|
|
100
102
|
<react_native_1.View style={[styles.statusDot, { backgroundColor: runtimeTone(phase, colors) }]}/>
|
|
101
103
|
<react_native_1.Text style={[styles.consoleTitle, { color: colors.text }]}>{title}</react_native_1.Text>
|
|
102
104
|
</react_native_1.View>
|
|
105
|
+
{sourceLabel ? <react_native_1.Text style={[styles.consoleSource, { color: colors.muted }]}>Source · {sourceLabel}</react_native_1.Text> : null}
|
|
103
106
|
<react_native_1.Text style={[styles.consoleStatus, { color: colors.muted }]}>{message}</react_native_1.Text>
|
|
104
107
|
{text ? (renderText ? renderText(text) : <react_native_1.Text selectable style={[styles.consoleText, { color: colors.text }]}>{text}</react_native_1.Text>) : (<react_native_1.Text style={[styles.consoleEmpty, { color: colors.muted }]}>Waiting for the first line from the remote PC…</react_native_1.Text>)}
|
|
105
108
|
{failure ? (<react_native_1.View style={[styles.failure, { borderColor: colors.blocked }]}>
|
|
@@ -126,6 +129,7 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
126
129
|
console: { width: '100%', maxHeight: 320, overflow: 'hidden', marginTop: 10, borderWidth: 1, borderRadius: 10, padding: 11, gap: 7 },
|
|
127
130
|
consoleHeader: { flexDirection: 'row', alignItems: 'center', gap: 7 },
|
|
128
131
|
consoleTitle: { fontSize: 12, fontWeight: '800' },
|
|
132
|
+
consoleSource: { fontSize: 10, lineHeight: 14 },
|
|
129
133
|
consoleStatus: { fontSize: 11, lineHeight: 16 },
|
|
130
134
|
consoleText: { fontFamily: 'monospace', fontSize: 10, lineHeight: 15 },
|
|
131
135
|
consoleEmpty: { fontSize: 10, fontStyle: 'italic' },
|