diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.tsx index 224e466e9b88f..444597b379770 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.tsx @@ -9,6 +9,7 @@ import { getFrameSource } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' import { noop as css } from '../../helpers/noop-template' import { ExternalIcon } from '../../icons/external' +import { FileIcon } from '../../icons/FileIcon' export type CodeFrameProps = { stackFrame: StackFrame; codeFrame: string } @@ -156,17 +157,3 @@ export const CODE_FRAME_STYLES = css` margin-right: 6px; } ` - -// TODO: Add more Icons (react, next, etc.) -function FileIcon() { - return ( - - - - ) -} diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/EditorLink.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/EditorLink.tsx index 518c29e4c7023..61386cc1d811d 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/EditorLink.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/EditorLink.tsx @@ -1,5 +1,5 @@ -import React from 'react' import { useOpenInEditor } from '../../helpers/use-open-in-editor' +import { noop as css } from '../../helpers/noop-template' type EditorLinkProps = { file: string @@ -48,3 +48,27 @@ export function EditorLink({ file, isSourceFile, location }: EditorLinkProps) { ) } + +export const EDITOR_LINK_STYLES = css` + [data-with-open-in-editor-link] svg { + width: auto; + height: var(--size-font-small); + margin-left: var(--size-gap); + } + [data-with-open-in-editor-link] { + cursor: pointer; + } + [data-with-open-in-editor-link]:hover { + text-decoration: underline dotted; + } + [data-with-open-in-editor-link-import-trace] { + margin-left: var(--size-gap-double); + } + [data-with-open-in-editor-link-source-file] { + border-bottom: 1px solid var(--color-ansi-bright-black); + display: flex; + align-items: center; + justify-content: space-between; + line-break: anywhere; + } +` diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.stories.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.stories.tsx new file mode 100644 index 0000000000000..b32ec6077ff45 --- /dev/null +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.stories.tsx @@ -0,0 +1,42 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { Terminal } from './Terminal' +import { withShadowPortal } from '../../storybook/with-shadow-portal' + +const meta: Meta = { + component: Terminal, + parameters: { + layout: 'fullscreen', + }, + decorators: [withShadowPortal], +} + +export default meta +type Story = StoryObj + +export const SimpleTerminal: Story = { + args: { + content: + './app/page.tsx:10:5\n\u001b[31mError:\u001b[39m Something went wrong\n at Home (./app/page.tsx:10:5)', + }, +} + +export const WithImportTrace: Story = { + args: { + content: `./components/Button.tsx:15:3 +ReactServerComponentsError: Failed to load component +Import trace for requested module: +./pages/index.tsx +./components/Layout.tsx +./components/Button.tsx`, + }, +} + +export const WithAnsiColors: Story = { + args: { + content: `./app/error.tsx:5:10 +\u001b[31m\u001b[1mError:\u001b[22m\u001b[39m Failed to compile +\u001b[36m console\u001b[39m.\u001b[33mlog\u001b[39m('Debug message') +\u001b[32m✓\u001b[39m Success message +\u001b[31m✕\u001b[39m Error message`, + }, +} diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.tsx index 57ab805437483..3e47633d6d1b4 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/Terminal.tsx @@ -2,6 +2,11 @@ import Anser from 'next/dist/compiled/anser' import * as React from 'react' import { HotlinkedText } from '../hot-linked-text' import { EditorLink } from './EditorLink' +import { ExternalIcon } from '../../icons/external' +import { noop as css } from '../../helpers/noop-template' +import { getFrameSource } from '../../helpers/stack-frame' +import { useOpenInEditor } from '../../helpers/use-open-in-editor' +import { FileIcon } from '../../icons/FileIcon' export type TerminalProps = { content: string } @@ -70,16 +75,37 @@ export const Terminal: React.FC = function Terminal({ }) }, [source]) + const open = useOpenInEditor({ + file: file?.fileName, + lineNumber: file?.location?.line, + column: file?.location?.column, + }) + + const stackFrame = { + file: file?.fileName ?? null, + methodName: '', + arguments: [], + lineNumber: file?.location?.line ?? null, + column: file?.location?.column ?? null, + } + return (
- {file && ( - - )} +
+

+ + + {getFrameSource(stackFrame)} @{' '} + + + +

+
         {decoded.map((entry, index) => (
            = function Terminal({
     
) } + +export const TERMINAL_STYLES = css` + [data-nextjs-terminal] { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + flex: 1 0 0; + + background-color: var(--color-background-200); + overflow: hidden; + color: var(--color-gray-1000); + text-overflow: ellipsis; + font-family: var(--font-stack-monospace); + font-size: 12px; + line-height: 16px; + } + + .terminal-header { + border-top: 1px solid var(--color-gray-400); + border-bottom: 1px solid var(--color-gray-400); + } + + [data-nextjs-terminal]::selection, + [data-nextjs-terminal] *::selection { + background-color: var(--color-ansi-selection); + } + + [data-nextjs-terminal] * { + color: inherit; + background-color: transparent; + font-family: var(--font-stack-monospace); + } + + [data-nextjs-terminal] > * { + margin: 0; + padding: calc(var(--size-gap) + var(--size-gap-half)) + calc(var(--size-gap-double) + var(--size-gap-half)); + } + + [data-nextjs-terminal] > div > p { + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + margin: 0; + } + [data-nextjs-terminal] > div > p:hover { + text-decoration: underline dotted; + } + [data-nextjs-terminal] div > pre { + overflow: hidden; + display: inline-block; + } + + [data-nextjs-terminal] svg { + color: var(--color-gray-900); + margin-right: 6px; + } +` diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/styles.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/styles.tsx deleted file mode 100644 index 5ed1fc4565138..0000000000000 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/Terminal/styles.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { noop as css } from '../../helpers/noop-template' - -const styles = css` - [data-nextjs-terminal] { - border-radius: var(--size-gap-half); - background-color: var(--color-ansi-bg); - color: var(--color-ansi-fg); - } - [data-nextjs-terminal]::selection, - [data-nextjs-terminal] *::selection { - background-color: var(--color-ansi-selection); - } - [data-nextjs-terminal] * { - color: inherit; - background-color: transparent; - font-family: var(--font-stack-monospace); - } - [data-nextjs-terminal] > * { - margin: 0; - padding: calc(var(--size-gap) + var(--size-gap-half)) - calc(var(--size-gap-double) + var(--size-gap-half)); - } - - [data-nextjs-terminal] pre { - white-space: pre-wrap; - word-break: break-word; - } - - [data-with-open-in-editor-link] svg { - width: auto; - height: var(--size-font-small); - margin-left: var(--size-gap); - } - [data-with-open-in-editor-link] { - cursor: pointer; - } - [data-with-open-in-editor-link]:hover { - text-decoration: underline dotted; - } - [data-with-open-in-editor-link-source-file] { - border-bottom: 1px solid var(--color-ansi-bright-black); - display: flex; - align-items: center; - justify-content: space-between; - line-break: anywhere; - } - [data-with-open-in-editor-link-import-trace] { - margin-left: var(--size-gap-double); - } - [data-nextjs-terminal] a { - color: inherit; - } -` - -export { styles } diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/icons/FileIcon.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/icons/FileIcon.tsx new file mode 100644 index 0000000000000..44a9d1e4bdc04 --- /dev/null +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/icons/FileIcon.tsx @@ -0,0 +1,19 @@ +// TODO: Add more Icons (react, next, etc.) +export function FileIcon(props: React.SVGProps) { + return ( + + + + ) +} diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx index ea76455226863..9f4198006e245 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/ComponentStyles.tsx @@ -5,7 +5,7 @@ import { styles as bottomStacks } from '../components/Errors/error-overlay-botto import { styles as pagination } from '../components/Errors/error-overlay-pagination/error-overlay-pagination' import { styles as overlay } from '../components/Overlay/styles' import { styles as footer } from '../components/Errors/error-overlay-footer/error-overlay-footer' -import { styles as terminal } from '../components/Terminal/styles' +import { TERMINAL_STYLES } from '../components/Terminal/Terminal' import { styles as toast } from '../components/Toast' import { styles as versionStaleness } from '../components/VersionStalenessInfo/VersionStalenessInfo' import { styles as buildErrorStyles } from '../container/BuildError' @@ -15,6 +15,7 @@ import { COPY_BUTTON_STYLES } from '../components/copy-button' import { CALL_STACK_FRAME_STYLES } from '../components/call-stack-frame/call-stack-frame' import { styles as devToolsIndicator } from '../components/Errors/dev-tools-indicator/styles' import { noop as css } from '../helpers/noop-template' +import { EDITOR_LINK_STYLES } from '../components/Terminal/EditorLink' export function ComponentStyles() { return ( @@ -30,7 +31,8 @@ export function ComponentStyles() { ${bottomStacks} ${pagination} ${CODE_FRAME_STYLES} - ${terminal} + ${TERMINAL_STYLES} + ${EDITOR_LINK_STYLES} ${buildErrorStyles} ${containerErrorStyles} ${containerRuntimeErrorStyles}