diff --git a/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js b/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js
index 3f3ae961968b..f76143a3e076 100644
--- a/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js
+++ b/packages/react-native/Libraries/LogBox/UI/LogBoxInspector.js
@@ -8,6 +8,7 @@
* @format
*/
+import Clipboard from '../../Components/Clipboard/Clipboard';
import Keyboard from '../../Components/Keyboard/Keyboard';
import View from '../../Components/View/View';
import StyleSheet from '../../StyleSheet/StyleSheet';
@@ -59,6 +60,47 @@ export default function LogBoxInspector(props: Props): React.Node {
LogBoxData.retrySymbolicateLogNow(log);
}
+ function _handleCopy() {
+ const headerTitleMap = {
+ warn: 'Console Warning',
+ error: 'Console Error',
+ fatal: 'Uncaught Error',
+ syntax: 'Syntax Error',
+ component: 'Render Error',
+ };
+
+ const title =
+ log.type ??
+ headerTitleMap[log.isComponentError ? 'component' : log.level];
+
+ const parts = [title, '', log.message.content];
+
+ if (log.codeFrame != null) {
+ const location = log.codeFrame.location;
+ parts.push(
+ '',
+ 'Source:',
+ location != null
+ ? `${log.codeFrame.fileName} (${location.row}:${location.column})`
+ : log.codeFrame.fileName,
+ );
+ }
+
+ const stack = log.getAvailableStack();
+ if (stack.length > 0) {
+ parts.push('', 'Call Stack:');
+ for (const frame of stack) {
+ const methodName = frame.methodName ?? '?';
+ const file = frame.file ?? '?';
+ const lineNumber =
+ frame.lineNumber != null ? `:${frame.lineNumber}` : '';
+ parts.push(`${methodName} (${file}${lineNumber})`);
+ }
+ }
+
+ Clipboard.setString(parts.join('\n'));
+ }
+
if (log == null) {
return null;
}
@@ -75,6 +117,7 @@ export default function LogBoxInspector(props: Props): React.Node {
diff --git a/packages/react-native/Libraries/LogBox/UI/LogBoxInspectorFooter.js b/packages/react-native/Libraries/LogBox/UI/LogBoxInspectorFooter.js
index 9f599c07e7d8..5d5092944e05 100644
--- a/packages/react-native/Libraries/LogBox/UI/LogBoxInspectorFooter.js
+++ b/packages/react-native/Libraries/LogBox/UI/LogBoxInspectorFooter.js
@@ -20,6 +20,7 @@ import * as React from 'react';
type Props = Readonly<{
onDismiss: () => void,
onMinimize: () => void,
+ onCopy: () => void,
level?: ?LogLevel,
}>;
@@ -48,6 +49,11 @@ export default function LogBoxInspectorFooter(props: Props): React.Node {
text="Minimize"
onPress={props.onMinimize}
/>
+
);
}
diff --git a/packages/react-native/Libraries/LogBox/UI/__tests__/LogBoxInspectorFooter-test.js b/packages/react-native/Libraries/LogBox/UI/__tests__/LogBoxInspectorFooter-test.js
index 9d60b64ec990..9a0cab402c5e 100644
--- a/packages/react-native/Libraries/LogBox/UI/__tests__/LogBoxInspectorFooter-test.js
+++ b/packages/react-native/Libraries/LogBox/UI/__tests__/LogBoxInspectorFooter-test.js
@@ -27,6 +27,7 @@ describe('LogBoxInspectorFooter', () => {
{}}
onDismiss={() => {}}
+ onCopy={() => {}}
level="warn"
/>,
);
@@ -39,6 +40,7 @@ describe('LogBoxInspectorFooter', () => {
{}}
onDismiss={() => {}}
+ onCopy={() => {}}
level="error"
/>,
);
@@ -51,6 +53,7 @@ describe('LogBoxInspectorFooter', () => {
{}}
onDismiss={() => {}}
+ onCopy={() => {}}
level="fatal"
/>,
);
@@ -63,6 +66,7 @@ describe('LogBoxInspectorFooter', () => {
{}}
onDismiss={() => {}}
+ onCopy={() => {}}
level="syntax"
/>,
);
diff --git a/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspector-test.js.snap b/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspector-test.js.snap
index e1ffa1836f24..2c87e93c11d5 100644
--- a/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspector-test.js.snap
+++ b/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspector-test.js.snap
@@ -49,6 +49,7 @@ exports[`LogBoxContainer should render fatal with selectedIndex 2 1`] = `
/>
@@ -106,6 +107,7 @@ exports[`LogBoxContainer should render warning with selectedIndex 0 1`] = `
/>
diff --git a/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorFooter-test.js.snap b/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorFooter-test.js.snap
index 272c6de898a8..57412c533f20 100644
--- a/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorFooter-test.js.snap
+++ b/packages/react-native/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorFooter-test.js.snap
@@ -71,6 +71,11 @@ exports[`LogBoxInspectorFooter should render two buttons for error 1`] = `
onPress={[Function]}
text="Minimize"
/>
+
`;
@@ -100,6 +105,11 @@ exports[`LogBoxInspectorFooter should render two buttons for fatal 1`] = `
onPress={[Function]}
text="Minimize"
/>
+
`;
@@ -129,5 +139,10 @@ exports[`LogBoxInspectorFooter should render two buttons for warning 1`] = `
onPress={[Function]}
text="Minimize"
/>
+
`;