diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.stories.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.stories.tsx new file mode 100644 index 0000000000000..1a720e5dd54ee --- /dev/null +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/CodeFrame.stories.tsx @@ -0,0 +1,35 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { CodeFrame } from './CodeFrame' +import { withShadowPortal } from '../../storybook/with-shadow-portal' + +const meta: Meta = { + title: 'CodeFrame', + component: CodeFrame, + parameters: { + layout: 'fullscreen', + }, + decorators: [withShadowPortal], +} + +export default meta +type Story = StoryObj + +const baseStackFrame = { + file: './app/page.tsx', + methodName: 'Home', + arguments: [], + lineNumber: 10, + column: 5, +} + +export const SimpleCodeFrame: Story = { + args: { + stackFrame: baseStackFrame, + codeFrame: `\u001b[0m \u001b[90m 1 \u001b[39m \u001b[36mexport\u001b[39m \u001b[36mdefault\u001b[39m \u001b[36mfunction\u001b[39m \u001b[33mHome\u001b[39m() {\u001b[0m +\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 2 \u001b[39m \u001b[36mthrow\u001b[39m \u001b[36mnew\u001b[39m \u001b[33mError\u001b[39m(\u001b[32m'boom'\u001b[39m)\u001b[0m +\u001b[0m \u001b[90m \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m +\u001b[0m \u001b[90m 3 \u001b[39m \u001b[36mreturn\u001b[39m \u001b[33m<\u001b[39m\u001b[33mdiv\u001b[39m\u001b[33m>\u001b[39m\u001b[33mHello\u001b[39m \u001b[33mWorld\u001b[39m\u001b[33m<\u001b[39m\u001b[33m/\u001b[39m\u001b[33mdiv\u001b[39m\u001b[33m>\u001b[39m\u001b[0m +\u001b[0m \u001b[90m 4 \u001b[39m }\u001b[0m +\u001b[0m \u001b[90m 5 \u001b[39m\u001b[0m`, + }, +} 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 299787dea29af..224e466e9b88f 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 @@ -1,19 +1,20 @@ -import Anser from 'next/dist/compiled/anser' -import * as React from 'react' import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' + +import Anser from 'next/dist/compiled/anser' import stripAnsi from 'next/dist/compiled/strip-ansi' + +import { useMemo } from 'react' +import { HotlinkedText } from '../hot-linked-text' import { getFrameSource } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' -import { HotlinkedText } from '../hot-linked-text' +import { noop as css } from '../../helpers/noop-template' +import { ExternalIcon } from '../../icons/external' export type CodeFrameProps = { stackFrame: StackFrame; codeFrame: string } -export const CodeFrame: React.FC = function CodeFrame({ - stackFrame, - codeFrame, -}) { +export function CodeFrame({ stackFrame, codeFrame }: CodeFrameProps) { // Strip leading spaces out of the code frame: - const formattedFrame = React.useMemo(() => { + const formattedFrame = useMemo(() => { const lines = codeFrame.split(/\r?\n/g) // Find the minimum length of leading spaces after `|` in the code frame @@ -34,7 +35,9 @@ export const CodeFrame: React.FC = function CodeFrame({ .map((line, a) => ~(a = line.indexOf('|')) ? line.substring(0, a) + - line.substring(a).replace(`^\\ {${miniLeadingSpacesLength}}`, '') + line + .substring(a + 1) + .replace(`^\\ {${miniLeadingSpacesLength}}`, '') : line ) .join('\n') @@ -42,7 +45,7 @@ export const CodeFrame: React.FC = function CodeFrame({ return lines.join('\n') }, [codeFrame]) - const decoded = React.useMemo(() => { + const decoded = useMemo(() => { return Anser.ansiToJson(formattedFrame, { json: true, use_classes: true, @@ -59,7 +62,7 @@ export const CodeFrame: React.FC = function CodeFrame({ // TODO: make the caret absolute return (
-
+

= function CodeFrame({ title="Click to open in your editor" > + {getFrameSource(stackFrame)} @{' '} - - - - - +

@@ -105,3 +97,76 @@ export const CodeFrame: React.FC = function CodeFrame({
     
) } + +export const CODE_FRAME_STYLES = css` + [data-nextjs-codeframe] { + 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; + } + + .code-frame-header { + border-top: 1px solid var(--color-gray-400); + border-bottom: 1px solid var(--color-gray-400); + } + + [data-nextjs-codeframe]::selection, + [data-nextjs-codeframe] *::selection { + background-color: var(--color-ansi-selection); + } + + [data-nextjs-codeframe] * { + color: inherit; + background-color: transparent; + font-family: var(--font-stack-monospace); + } + + [data-nextjs-codeframe] > * { + margin: 0; + padding: calc(var(--size-gap) + var(--size-gap-half)) + calc(var(--size-gap-double) + var(--size-gap-half)); + } + + [data-nextjs-codeframe] > div > p { + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + margin: 0; + } + [data-nextjs-codeframe] > div > p:hover { + text-decoration: underline dotted; + } + [data-nextjs-codeframe] div > pre { + overflow: hidden; + display: inline-block; + } + + [data-nextjs-codeframe] svg { + color: var(--color-gray-900); + 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/CodeFrame/index.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/index.tsx deleted file mode 100644 index 59818df56eddb..0000000000000 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { CodeFrame } from './CodeFrame' diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/styles.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/styles.tsx deleted file mode 100644 index 2b86f7652ba14..0000000000000 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/CodeFrame/styles.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { noop as css } from '../../helpers/noop-template' - -const styles = css` - [data-nextjs-codeframe] { - overflow: auto; - border-radius: var(--size-gap-half); - background-color: var(--color-ansi-bg); - color: var(--color-ansi-fg); - margin-bottom: var(--size-gap-double); - } - [data-nextjs-codeframe]::selection, - [data-nextjs-codeframe] *::selection { - background-color: var(--color-ansi-selection); - } - [data-nextjs-codeframe] * { - color: inherit; - background-color: transparent; - font-family: var(--font-stack-monospace); - } - - [data-nextjs-codeframe] > * { - margin: 0; - padding: calc(var(--size-gap) + var(--size-gap-half)) - calc(var(--size-gap-double) + var(--size-gap-half)); - } - [data-nextjs-codeframe] > div { - display: inline-block; - width: auto; - min-width: 100%; - border-bottom: 1px solid var(--color-ansi-bright-black); - } - [data-nextjs-codeframe] > div > p { - display: flex; - align-items: center; - justify-content: space-between; - cursor: pointer; - margin: 0; - } - [data-nextjs-codeframe] > div > p:hover { - text-decoration: underline dotted; - } - [data-nextjs-codeframe] div > p > svg { - width: auto; - height: 1em; - margin-left: 8px; - } - [data-nextjs-codeframe] div > pre { - overflow: hidden; - display: inline-block; - } -` - -export { styles } diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/call-stack-frame/call-stack-frame.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/call-stack-frame/call-stack-frame.tsx index 2a8cfe64a1084..a0c1084183b85 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/call-stack-frame/call-stack-frame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/components/call-stack-frame/call-stack-frame.tsx @@ -2,6 +2,7 @@ import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import type { OriginalStackFrame } from '../../helpers/stack-frame' import { HotlinkedText } from '../hot-linked-text' +import { ExternalIcon } from '../../icons/external' import { getFrameSource } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' import { noop as css } from '../../helpers/noop-template' @@ -50,7 +51,7 @@ export const CallStackFrame: React.FC<{ className="call-stack-frame-method-name" > - {hasSource && } + {hasSource && } - - - ) -} - export const CALL_STACK_FRAME_STYLES = css` [data-nextjs-call-stack-frame-ignored] { padding: var(--size-1_5) var(--size-2); diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/RuntimeError/index.tsx index 47abcbd25758f..d03007520ea2d 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/container/RuntimeError/index.tsx @@ -1,7 +1,7 @@ import type { ReadyRuntimeError } from '../../helpers/get-error-by-type' import { useMemo } from 'react' -import { CodeFrame } from '../../components/CodeFrame' +import { CodeFrame } from '../../components/CodeFrame/CodeFrame' import { CallStack } from '../../components/Errors/call-stack/call-stack' import { noop as css } from '../../helpers/noop-template' import { PSEUDO_HTML_DIFF_STYLES } from './component-stack-pseudo-html' diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/icons/external.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/icons/external.tsx new file mode 100644 index 0000000000000..ae4231b6fb886 --- /dev/null +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/icons/external.tsx @@ -0,0 +1,12 @@ +export function ExternalIcon(props: React.SVGProps) { + return ( + + + + ) +} diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx index 28062f96a8d7c..5e55f053a9763 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/Base.tsx @@ -44,27 +44,6 @@ export function Base() { --font-stack-sans: 'Geist', -apple-system, 'Source Sans Pro', sans-serif; - --color-ansi-selection: rgba(95, 126, 151, 0.48); - --color-ansi-bg: #111111; - --color-ansi-fg: #cccccc; - - --color-ansi-white: #777777; - --color-ansi-black: #141414; - --color-ansi-blue: #00aaff; - --color-ansi-cyan: #88ddff; - --color-ansi-green: #98ec65; - --color-ansi-magenta: #aa88ff; - --color-ansi-red: #ff5555; - --color-ansi-yellow: #ffcc33; - --color-ansi-bright-white: #ffffff; - --color-ansi-bright-black: #777777; - --color-ansi-bright-blue: #33bbff; - --color-ansi-bright-cyan: #bbecff; - --color-ansi-bright-green: #b6f292; - --color-ansi-bright-magenta: #cebbff; - --color-ansi-bright-red: #ff8888; - --color-ansi-bright-yellow: #ffd966; - font-family: var(--font-stack-sans); /* TODO: Remove replaced ones. */ 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 1e0d51bfd895b..ea76455226863 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 @@ -1,4 +1,4 @@ -import { styles as codeFrame } from '../components/CodeFrame/styles' +import { CODE_FRAME_STYLES } from '../components/CodeFrame/CodeFrame' import { styles as dialog } from '../components/Dialog' import { styles as errorLayout } from '../components/Errors/error-overlay-layout/error-overlay-layout' import { styles as bottomStacks } from '../components/Errors/error-overlay-bottom-stacks/error-overlay-bottom-stacks' @@ -29,7 +29,7 @@ export function ComponentStyles() { ${footer} ${bottomStacks} ${pagination} - ${codeFrame} + ${CODE_FRAME_STYLES} ${terminal} ${buildErrorStyles} ${containerErrorStyles} diff --git a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx index d5501f754edca..44f1393c19d8d 100644 --- a/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx +++ b/packages/next/src/client/components/react-dev-overlay/_experimental/internal/styles/colors.tsx @@ -6,6 +6,28 @@ export function Colors() {