m './remove-path-prefix'
import { pathHasPrefix } from './path-has-prefix'
import type { I18NProvider } from '../../../../server/lib/i18n-provider'

export interface NextPathnameInfo {
  /**
   * The base path in case the pathname included it.
   */
  basePath?: string
  /**
   * The buildId for when the parsed URL is a data URL. Parsing it can be
   * disabled with the `parseData` option.
   */
  buildId?: string
  /**
   * If there was a locale in the pathname, this will hold its value.
   */
  locale?: s

y
   * true if trailingSlash is enabled.
   */
  trailingSlash?: boolean
}

interface Options {
  /**
   * When passed to true, this function will also parse Nextjs data URLs.
   */
  parseData?: boolean
  /**
   * A partial of the Next.js configuration to

tring
  /**
   * The processed pathname without a base path, locale, or data URL elements
   * when parsing it is enabled.
   */
  pathname: string
  /**
   * A boolean telling if the pathname had a trailingSlash. This can be onl


 '../app-router-types'

// TypeScript trick to simulate opaque types, like in Flow.
type Opaque<K, T> = T & { __brand: K }

export type SegmentRequestKeyPart = Opaque<'SegmentRequestKeyPart', string>
export type SegmentRequestKey = Opaque<'SegmentRequestKey', string>

export const ROOT_SEGMENT_REQUEST_KEY = '' as SegmentRequestKey

export const HEAD_REQUEST_KEY = '/_head' as SegmentRequestKey

export function createSegmentRequestKeyPart(
  segment: FlightRouterStateSegment
): SegmentRequestKeyPart {
  if (t

parate
      // key. So, we strip the search params here, and then add them back when
      // the cache entry is turned back into a FlightRouterState. This is an
      // unfortunate consequence of the FlightRouteState being used both as a
      // transp

ypeof segment === 'string') {
    if (segment.startsWith(PAGE_SEGMENT_KEY)) {
      // The Flight Router State type sometimes includes the search params in
      // the page segment. However, the Segment Cache tracks this as a se


} from './router'
import { adaptForAppRouterInstance } from './adapters'

describe('adaptForAppRouterInstance', () => {
  beforeEach(() => jest.resetAllMocks())

  const router = {
    back: jest.fn(),
    forward: jest.fn(),
    reload: jest.fn(),
    push: jest.fn(),
    replace: jest.fn(),
    prefetch: jest.fn(),
  } as unknown as NextRouter

  const adapter = adaptForAppRouterInstance(router)

  it('should forward a call to `back()`', () => {
    adapter.back()
    expect(router.back).toHaveBeenCalled(

load).toHaveBeenCalled()
  })

  it('should forward a call to `push()`', () => {
    adapter.push('/foo')
    expect(router.push).toHaveBeenCalledWith('/foo', undefined, {
      scroll: undefined,
    })
  })

  it('should forward a call to `push()` with o

)
  })

  it('should forward a call to `forward()`', () => {
    adapter.forward()
    expect(router.forward).toHaveBeenCalled()
  })

  it('should forward a call to `reload()`', () => {
    adapter.refresh()
    expect(router.re


'../../utils/warn-once'

/**
 * Run function with `scroll-behavior: auto` applied to `<html/>`.
 * This css change will be reverted after the function finishes.
 */
export function disableSmoothScrollDuringRouteTransition(
  fn: () => void,
  options: { dontForceLayout?: boolean; onlyHashChange?: boolean } = {}
) {
  // if only the hash is changed, we don't need to disable smooth scrolling
  // we only care to prevent smooth scrolling when navigating to a new page to avoid jarring UX
  if (options.onlyHashC

ut no data attribute is present
    if (
      process.env.NODE_ENV === 'development' &&
      getComputedStyle(htmlElement).scrollBehavior === 'smooth'
    ) {
      warnOnce(
        'Detected `scroll-behavior: smooth` on the `<html>` element. To disable

hange) {
    fn()
    return
  }

  const htmlElement = document.documentElement
  const hasDataAttribute = htmlElement.dataset.scrollBehavior === 'smooth'

  if (!hasDataAttribute) {
    // Warn if smooth scrolling is detected b


port const INTERCEPTION_ROUTE_MARKERS = [
  '(..)(..)',
  '(.)',
  '(..)',
  '(...)',
] as const

export type InterceptionMarker = (typeof INTERCEPTION_ROUTE_MARKERS)[number]

export function isInterceptionRouteAppPath(path: string): boolean {
  // TODO-APP: add more serious validation
  return (
    path
      .split('/')
      .find((segment) =>
        INTERCEPTION_ROUTE_MARKERS.find((m) => segment.startsWith(m))
      ) !== undefined
  )
}

type InterceptionRouteInformation = {
  /**
   * The intercepti

route that is being intercepted or the
   * route that the user is going to. This is matched by the request pathname.
   */
  interceptedRoute: string
}

export function extractInterceptionRouteInformation(
  path: string
): InterceptionRouteInformation {

ng route. This is the route that is being intercepted or the
   * route that the user was coming from. This is matched by the Next-Url
   * header.
   */
  interceptingRoute: string

  /**
   * The intercepted route. This is the


ception-routes'
import type { DynamicParamTypes } from '../../app-router-types'

export type SegmentParam = {
  paramName: string
  paramType: DynamicParamTypes
}

/**
 * Parse dynamic route segment to type of parameter
 */
export function getSegmentParam(segment: string): SegmentParam | null {
  const interceptionMarker = INTERCEPTION_ROUTE_MARKERS.find((marker) =>
    segment.startsWith(marker)
  )

  // if an interception marker is part of the path segment, we need to jump ahead
  // to the relevant port

ot currently work with parallel routes,
      // so for now aren't handling a potential interception marker.
      paramType: 'optional-catchall',
      paramName: segment.slice(5, -2),
    }
  }

  if (segment.startsWith('[...') && segment.endsWith(']'))

ion for param parsing
  if (interceptionMarker) {
    segment = segment.slice(interceptionMarker.length)
  }

  if (segment.startsWith('[[...') && segment.endsWith(']]')) {
    return {
      // TODO-APP: Optional catchall does n


IFF_STYLES = `
  [data-nextjs-container-errors-pseudo-html] {
    padding: 8px 0;
    margin: 8px 0;
    border: 1px solid var(--color-gray-400);
    background: var(--color-background-200);
    color: var(--color-syntax-constant);
    font-family: var(--font-stack-monospace);
    font-size: var(--size-12);
    line-height: 1.33em; /* 16px in 12px font size */
    border-radius: var(--rounded-md-2);
  }
  [data-nextjs-container-errors-pseudo-html-line] {
    display: inline-block;
    width: 100%;
    paddi

  }
  [data-nextjs-container-errors-pseudo-html-collapse-button] {
    all: unset;
    margin-left: 12px;
    &:focus {
      outline: none;
    }
  }
  [data-nextjs-container-errors-pseudo-html--diff='add'] {
    background: var(--color-green-300);
  }

ng-left: 40px;
    line-height: calc(5 / 3);
  }
  [data-nextjs-container-errors-pseudo-html--diff='error'] {
    background: var(--color-amber-100);
    box-shadow: 2px 0 0 0 var(--color-amber-900) inset;
    font-weight: bold;


code-frame/code-frame'
import { ErrorOverlayCallStack } from '../../components/errors/error-overlay-call-stack/error-overlay-call-stack'
import { ErrorCause } from './error-cause'
import { HotlinkedText } from '../../components/hot-linked-text'
import type { ReadyErrorCause } from '../../utils/get-error-by-type'

interface ErrorAggregateErrorsProps {
  errors: ReadyErrorCause[]
  dialogResizerRef: React.RefObject<HTMLDivElement | null>
}

export function ErrorAggregateErrors({
  errors,
  dialogResizerRef,

  total={errors.length}
          dialogResizerRef={dialogResizerRef}
        />
      ))}
    </>
  )
}

interface ErrorAggregateEntryProps {
  entry: ReadyErrorCause
  index: number
  total: number
  dialogResizerRef: React.RefObject<HTMLDivElement | nul

}: ErrorAggregateErrorsProps) {
  return (
    // TODO: Wrap in SuspenseList
    <>
      {errors.map((entry, index) => (
        <ErrorAggregateEntry
          key={index}
          entry={entry}
          index={index}


react'
import { CodeFrame } from '../../components/code-frame/code-frame'
import { ErrorOverlayCallStack } from '../../components/errors/error-overlay-call-stack/error-overlay-call-stack'
import { ErrorAggregateErrors } from './error-aggregate-errors'
import { HotlinkedText } from '../../components/hot-linked-text'
import type { ReadyErrorCause } from '../../utils/get-error-by-type'

type ErrorCauseProps = {
  cause: ReadyErrorCause
  dialogResizerRef: React.RefObject<HTMLDivElement | null>
}

export functi

(
      (entry) =>
        !entry.ignored &&
        Boolean(entry.originalCodeFrame) &&
        Boolean(entry.originalStackFrame)
    )
    return frames[index] ?? null
  }, [frames])

  return (
    <div data-nextjs-error-cause>
      <div className="err

on ErrorCause({ cause, dialogResizerRef }: ErrorCauseProps) {
  const frames = React.use(cause.frames())
  const trimmedMessage = cause.error.message.trim()

  const firstFrame = useMemo(() => {
    const index = frames.findIndex


react'
import { CodeFrame } from '../../components/code-frame/code-frame'
import { ErrorOverlayCallStack } from '../../components/errors/error-overlay-call-stack/error-overlay-call-stack'
import { PSEUDO_HTML_DIFF_STYLES } from './component-stack-pseudo-html'
import { ErrorCause, styles as errorCauseStyles } from './error-cause'
import {
  ErrorAggregateErrors,
  styles as errorAggregateErrorsStyles,
} from './error-aggregate-errors'
import {
  useFrames,
  type ReadyRuntimeError,
} from '../../utils/get-er

Frames(error)

  const firstFrame = useMemo(() => {
    const firstFirstPartyFrameIndex = frames.findIndex(
      (entry) =>
        !entry.ignored &&
        Boolean(entry.originalCodeFrame) &&
        Boolean(entry.originalStackFrame)
    )

    return f

ror-by-type'

type RuntimeErrorProps = {
  error: ReadyRuntimeError
  dialogResizerRef: React.RefObject<HTMLDivElement | null>
}

export function RuntimeError({ error, dialogResizerRef }: RuntimeErrorProps) {
  const frames = use


port type { StackFrame } from '../../../shared/stack-frame'

import { useMemo, useState, useEffect } from 'react'
import {
  getErrorByType,
  type ReadyRuntimeError,
} from '../../utils/get-error-by-type'

export type SupportedErrorEvent = {
  id: number
  error: Error
  frames: readonly StackFrame[]
  type: 'runtime' | 'recoverable' | 'console'
}

type Props = {
  children: (params: {
    runtimeErrors: ReadyRuntimeError[]
    totalErrorCount: number
  }) => React.ReactNode
  state: OverlayState
  isAppDi

meError {...props} />
  }
}

const RenderRuntimeError = ({ children, state, isAppDir }: Props) => {
  const { errors } = state

  const [lookups, setLookups] = useState<{
    [eventId: string]: ReadyRuntimeError
  }>({})

  const [runtimeErrors, nextError]

r: boolean
}

export const RenderError = (props: Props) => {
  const { state } = props
  const isBuildError = !!state.buildError

  if (isBuildError) {
    return <RenderBuildError {...props} />
  } else {
    return <RenderRunti


{ ReadyRuntimeError } from '../utils/get-error-by-type'
import type { HydrationErrorState } from '../../shared/hydration-error'

import { useMemo, useState } from 'react'
import { getErrorTypeLabel, useErrorDetails } from '../container/errors'
import { extractNextErrorCode } from '../../../lib/error-telemetry-utils'

export function useActiveRuntimeError({
  runtimeErrors,
  getSquashedHydrationErrorDetails,
}: {
  runtimeErrors: ReadyRuntimeError[]
  getSquashedHydrationErrorDetails: (error: Error) => Hydr

<ReadyRuntimeError | null>(
    () => runtimeErrors[activeIdx] ?? null,
    [activeIdx, runtimeErrors]
  )

  const errorDetails = useErrorDetails(
    activeError?.error,
    getSquashedHydrationErrorDetails
  )

  if (isLoading || !activeError) {
    ret

ationErrorState | null
}) {
  const [activeIdx, setActiveIndex] = useState<number>(0)

  const isLoading = useMemo<boolean>(() => {
    return runtimeErrors.length === 0
  }, [runtimeErrors.length])

  const activeError = useMemo


ile */
import { act, renderHook } from '@testing-library/react'
import { useDebouncedValue } from './use-debounced-value'

beforeEach(() => {
  jest.useFakeTimers()
})

afterEach(() => {
  jest.useRealTimers()
})

describe('useDebouncedValue', () => {
  it('returns the initial value immediately', () => {
    const { result } = renderHook(() => useDebouncedValue('a', 300))
    expect(result.current).toBe('a')
  })

  it('does not update immediately when value changes', () => {
    const { result, rerender }

> {
    const { result, rerender } = renderHook(
      ({ value }) => useDebouncedValue(value, 300),
      { initialProps: { value: 'a' } }
    )

    rerender({ value: 'b' })
    act(() => {
      jest.advanceTimersByTime(300)
    })
    expect(result.cur

= renderHook(
      ({ value }) => useDebouncedValue(value, 300),
      { initialProps: { value: 'a' } }
    )

    rerender({ value: 'b' })
    expect(result.current).toBe('a')
  })

  it('updates after the debounce delay', () =


ate } from 'react'

interface Options<T> {
  /**
   * Optional predicate evaluated when `value` changes. If it returns `true`
   * for the current transition, the new value is committed synchronously,
   * bypassing the debounce. Useful for "smooth subsequent updates but never
   * delay the first one" patterns.
   *
   * Should be a stable reference (module-level or `useCallback`-wrapped) to
   * avoid re-running the effect on every render.
   */
  leading?: (prev: T, next: T) => boolean
}

/**
 * Returns

 delayed
 * by `ms` unless `options.leading` returns `true` for the transition.
 */
export function useDebouncedValue<T>(
  value: T,
  ms: number,
  options: Options<T> = {}
): T {
  const [debounced, setDebounced] = useState(value)
  const { leading } =

a trailing-edge debounced version of `value`. When `value` changes,
 * the returned value is updated only after `ms` has elapsed without further
 * changes.
 *
 * The first value is committed synchronously. Subsequent changes are


ect } from 'react'

interface Options {
  enterDelay?: number
  exitDelay?: number
  onUnmount?: () => void
}

type Timeout = ReturnType<typeof setTimeout>

/**
 * Useful to perform CSS transitions on React components without
 * using libraries like Framer Motion. This hook will defer the
 * unmount of a React component until after a delay.
 *
 * @param active - Whether the component should be rendered
 * @param options - Options for the delayed render
 * @param options.enterDelay - Delay before rendering t

urn null
 *
 * return (
 *   <Portal>
 *
...

 *   </Portal>
 * )
 *}
 *
 * */
export function useDelayedRender(active = false, options: Options = {}) {
  const [mounted, setMounted] = useState

he component
 * @param options.exitDelay - Delay before unmounting the component
 *
 * const Modal = ({ active }) => {
 * const { mounted, rendered } = useDelayedRender(active, {
 *  exitDelay: 2000,
 * })
 *
 * if (!mounted) ret


React from 'react'

export function useOnClickOutside(
  el: Node | React.RefObject<Node | null> | null,
  cssSelectorsToExclude: string[],
  handler: ((e: MouseEvent | TouchEvent) => void) | undefined
) {
  React.useEffect(() => {
    // Support both direct nodes and ref objects
    const element = el && 'current' in el ? el.current : el
    if (element == null || handler == null) {
      return
    }

    const listener = (e: MouseEvent | TouchEvent) => {
      // Do nothing if clicking ref's element or d

clude.some((cssSelector) =>
          (e.target as Element).closest(cssSelector)
        )
      ) {
        return
      }

      handler(e)
    }

    const root = element.getRootNode()
    root.addEventListener('mouseup', listener as EventListener)

escendent elements
      if (!element || element.contains(e.target as Element)) {
        return
      }

      if (
        // Do nothing if clicking on an element that is excluded by the CSS selector(s)
        cssSelectorsToEx


 'react'
import { getActiveElement } from '../components/errors/dev-tools-indicator/utils'

export function useShortcuts(
  shortcuts: Record<string, () => void>,
  rootRef: React.RefObject<HTMLElement | null>
) {
  useEffect(() => {
    function handleKeyDown(e: KeyboardEvent) {
      if (isFocusedOnElement(rootRef)) return

      const keys = []

      if (e.metaKey) keys.push('Meta')
      if (e.ctrlKey) keys.push('Control')
      if (e.altKey) keys.push('Alt')
      if (e.shiftKey) keys.push('Shift')

rtcut]) {
        e.preventDefault()
        shortcuts[shortcut]()
      }
    }

    window.addEventListener('keydown', handleKeyDown)
    return () => window.removeEventListener('keydown', handleKeyDown)
  }, [rootRef, shortcuts])
}

function isFocusedOn

     if (
        e.key !== 'Meta' &&
        e.key !== 'Control' &&
        e.key !== 'Alt' &&
        e.key !== 'Shift'
      ) {
        keys.push(e.code)
      }

      const shortcut = keys.join('+')

      if (shortcuts[sho


dle,
  DragProvider,
} from '../components/errors/dev-tools-indicator/drag-context'
import { Draggable } from '../components/errors/dev-tools-indicator/draggable'
import { useClickOutsideAndEscape } from '../components/errors/dev-tools-indicator/utils'
import { usePanelRouterContext } from '../menu/context'
import { usePanelContext } from '../menu/panel-router'
import {
  ACTION_DEVTOOLS_PANEL_POSITION,
  STORAGE_KEY_PANEL_POSITION_PREFIX,
  STORE_KEY_PANEL_SIZE_PREFIX,
  STORE_KEY_SHARED_PANEL_LOCATION,

alue(
  value: string | number,
  dimension: 'width' | 'height' = 'width'
): number {
  if (typeof value === 'number') return value

  // kinda hacky, might be a better way to do this
  const temp = document.createElement('div')
  temp.style.position = 'ab

STORE_KEY_SHARED_PANEL_SIZE,
} from '../shared'
import { getIndicatorOffset } from '../utils/indicator-metrics'
import { saveDevToolsConfig } from '../utils/save-devtools-config'
import './dynamic-panel.css'

function resolveCSSV


aveDevToolsConfig } from '../../../utils/save-devtools-config'

export const ResizeHandle = ({
  direction,
  position,
}: {
  direction: ResizeDirection
  position: Corners
}) => {
  const {
    resizeRef,
    minWidth,
    minHeight,
    maxWidth,
    maxHeight,
    storageKey,
    draggingDirection,
    setDraggingDirection,
  } = useResize()
  const [borderWidths, setBorderWidths] = useState({
    top: 0,
    right: 0,
    bottom: 0,
    left: 0,
  })

  // TODO: NEXT-4645
  const shouldShowHandle = ()

ttom-left':
          return 'top-right'
        case 'bottom-right':
          return 'top-left'
        default: {
          corner satisfies never
          return null!
        }
      }
    }

    // we block the sides of the corner its in (bottom-lef

=> {
    const getOppositeCorner = (corner: Corners): ResizeDirection => {
      switch (corner) {
        case 'top-left':
          return 'bottom-right'
        case 'top-right':
          return 'bottom-left'
        case 'bo


,
  type RefObject,
} from 'react'
import { STORE_KEY_SHARED_PANEL_SIZE, type Corners } from '../../../shared'

export type ResizeDirection =
  | 'top'
  | 'right'
  | 'bottom'
  | 'left'
  | 'top-left'
  | 'top-right'
  | 'bottom-left'
  | 'bottom-right'

interface ResizeContextValue {
  resizeRef: RefObject<HTMLElement | null>
  minWidth: number
  minHeight: number
  maxWidth?: number
  maxHeight?: number
  draggingDirection: ResizeDirection | null
  setDraggingDirection: (direction: ResizeDirection | nul

Width = window.innerWidth * 0.95
  const maxHeight = window.innerHeight * 0.95

  return {
    width: Math.min(maxWidth, Math.max(params.minWidth, params.width)),
    height: Math.min(maxHeight, Math.max(params.minHeight, params.height)),
  }
}

interface

l) => void
  storageKey: string
}

const ResizeContext = createContext<ResizeContextValue>(null!)

const constrainDimensions = (params: {
  width: number
  height: number
  minWidth: number
  minHeight: number
}) => {
  const max


ct'
import { VersionStalenessInfo } from './version-staleness-info'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof VersionStalenessInfo> = {
  component: VersionStalenessInfo,
  parameters: {
    layout: 'centered',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof VersionStalenessInfo>

// Mock version info for different scenarios
const mockVersionInfo = {
  fresh: {
    installed: '15.0.0',
    ex

    staleness: 'stale-minor' as const,
  },
  staleMajor: {
    installed: '14.0.0',
    expected: '15.0.0',
    staleness: 'stale-major' as const,
  },
  stalePrerelease: {
    installed: '15.0.0-canary.0',
    expected: '15.0.0-canary.1',
    staleness:

pected: '15.0.0',
    staleness: 'fresh' as const,
  },
  stalePatch: {
    installed: '15.0.0',
    expected: '15.0.1',
    staleness: 'stale-patch' as const,
  },
  staleMinor: {
    installed: '15.0.0',
    expected: '15.1.0',


rom '../../../shared/version-staleness'
import { cx } from '../../utils/cx'
import { EclipseIcon } from '../../icons/eclipse'

export function VersionStalenessInfo({
  versionInfo,
  bundlerName,
}: {
  versionInfo: VersionInfo
  // Passed from parent for easier handling in Storybook.
  bundlerName: 'Webpack' | 'Turbopack' | 'Rspack'
}) {
  const { staleness } = versionInfo
  let { text, indicatorClass, title } = getStaleness(versionInfo)

  const isTurbopack = bundlerName === 'Turbopack'
  const shouldBeLi

r noreferrer"
        href="https://nextjs.org/docs/messages/version-staleness"
      >
        <EclipseIcon
          className={cx('version-staleness-indicator', indicatorClass)}
        />
        <span data-nextjs-version-checker title={title}>

nk = staleness.startsWith('stale')
  if (shouldBeLink) {
    return (
      <a
        className="nextjs-container-build-error-version-status dialog-exclude-closing-from-outside-click"
        target="_blank"
        rel="noopene


Obj } from '@storybook/react'

import { DevToolsIndicator } from './devtools-indicator'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'
import { withDevOverlayContexts } from '../../../../../.storybook/decorators/with-dev-overlay-contexts'
import { INITIAL_OVERLAY_STATE, type OverlayState } from '../../shared'

const meta: Meta<typeof DevToolsIndicator> = {
  component: DevToolsIndicator,
  parameters: {
    layout: 'centered',
  },
  argTypes: {},
  decorators: [

: 9999,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          background:
            'linear-gradient(135deg, rgba(230,240,255,0.8) 0%, rgba(200,220,255,0.6) 100%)',
        }}
      >
        <Story />

    withShadowPortal,
    // Test for high z-index
    (Story) => (
      <div
        style={{
          position: 'fixed',
          top: 0,
          left: 0,
          width: '100%',
          height: '100%',
          zIndex


icatorPosition } from '../../shared'
import { NextLogo } from './next-logo'
import { Toast } from '../toast'
import {
  MENU_CURVE,
  MENU_DURATION_MS,
} from '../errors/dev-tools-indicator/utils'
import {
  ACTION_DEVTOOLS_POSITION,
  STORE_KEY_SHARED_PANEL_LOCATION,
  STORAGE_KEY_PANEL_POSITION_PREFIX,
  ACTION_DEVTOOLS_PANEL_POSITION,
} from '../../shared'
import { Draggable } from '../errors/dev-tools-indicator/draggable'
import { useDevOverlayContext } from '../../../dev-overlay.browser'
import { usePa

erlayContext()
  const { panel, setPanel, setSelectedIndex } = usePanelRouterContext()
  const updateAllPanelPositions = useUpdateAllPanelPositions()
  const [vertical, horizontal] = state.devToolsPosition.split('-', 2)

  return (
    // TODO: why is this

nelRouterContext } from '../../menu/context'
import { saveDevToolsConfig } from '../../utils/save-devtools-config'

export const INDICATOR_PADDING = 20

export function DevToolsIndicator() {
  const { state, dispatch } = useDevOv


ct'
import { NextLogo } from './next-logo'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'
import { withDevOverlayContexts } from '../../../../../.storybook/decorators/with-dev-overlay-contexts'

const meta: Meta<typeof NextLogo> = {
  component: NextLogo,
  parameters: {
    layout: 'centered',
  },
  args: {
    'aria-label': 'Open Next.js DevTools',
    onTriggerClick: () => console.log('Logo clicked'),
  },
  decorators: [
    (Story) => (
      <div style={{ p

talErrorCount: 0,
      state: {
        buildingIndicator: false,
        renderingIndicator: false,
        cacheIndicator: 'ready',
      },
    }),
  ],
}

export const Compiling: Story = {
  decorators: [
    withDevOverlayContexts({
      totalErrorC

adding: '2rem' }}>
        <Story />
      </div>
    ),
    withShadowPortal,
  ],
}

export default meta
type Story = StoryObj<typeof NextLogo>

export const Idle: Story = {
  decorators: [
    withDevOverlayContexts({
      to


rom './status-indicator'

const SHORT_DURATION_MS = 150

// Smooth out rapid status transitions driven by bursty HMR events (e.g.
// Compiling→None→Compiling when consecutive compile episodes are <300ms apart,
// or Compiling→Rendering→Compiling oscillation). The debounce bridges burst
// gaps and active↔active flicker.
const STATUS_DEBOUNCE_MS = 300

export function NextLogo({
  onTriggerClick,
  ...buttonProps
}: { onTriggerClick: () => void } & React.ComponentProps<'button'>) {
  const { state, dispatch

selector'

  const hasError = totalErrorCount > 0
  const [isErrorExpanded, setIsErrorExpanded] = useState(hasError)
  const [previousHasError, setPreviousHasError] = useState(hasError)
  if (previousHasError !== hasError) {
    setPreviousHasError(hasErro

} = useDevOverlayContext()
  const { totalErrorCount } = useRenderErrorContext()
  const SIZE = BASE_LOGO_SIZE / state.scale
  const { panel, triggerRef, setPanel } = usePanelRouterContext()
  const isMenuOpen = panel === 'panel-


dering = 'prerendering',
  CacheBypassing = 'cache-bypassing',
}

export function getCurrentStatus(
  buildingIndicator: boolean,
  renderingIndicator: boolean,
  cacheIndicator: CacheIndicatorState
): Status {
  const isCacheFilling = cacheIndicator === 'filling'

  // Priority order: compiling > prerendering > rendering
  // Note: cache bypassing is now handled as a badge, not a status indicator
  if (buildingIndicator) {
    return Status.Compiling
  }
  if (isCacheFilling) {
    return Status.Prerenderi

icatorProps) {
  const statusText: Record<Status, string> = {
    [Status.None]: '',
    [Status.CacheBypassing]: 'Cache disabled',
    [Status.Prerendering]: 'Prerendering',
    [Status.Compiling]: 'Compiling',
    [Status.Rendering]: 'Rendering',
  }

ng
  }
  if (renderingIndicator) {
    return Status.Rendering
  }
  return Status.None
}

interface StatusIndicatorProps {
  status: Status
  onClick?: () => void
}

export function StatusIndicator({ status, onClick }: StatusInd


/**
 * A React hook that ensures a loading state persists
 * at least up to the next multiple of a given interval (default: 750ms).
 *
 * For example, if you're done loading at 1200ms, it forces you to wait
 * until 1500ms. If it’s 1800ms, it waits until 2250ms, etc.
 *
 * @param isLoadingTrigger - Boolean that triggers the loading state
 * @param interval - The time interval multiple in ms (default: 750ms)
 * @returns Current loading state that respects multiples of the interval
 */
export function useMi

 null>(null)

  useEffect(() => {
    // Clear any pending timeout to avoid overlap
    if (timeoutIdRef.current) {
      clearTimeout(timeoutIdRef.current)
      timeoutIdRef.current = null
    }

    if (isLoadingTrigger) {
      // If we enter "loading"

nimumLoadingTimeMultiple(
  isLoadingTrigger: boolean,
  interval = 750
) {
  const [isLoading, setIsLoading] = useState(false)
  const loadStartTimeRef = useRef<number | null>(null)
  const timeoutIdRef = useRef<NodeJS.Timeout |


ort { useEffect, useRef, useState } from 'react'

export function useUpdateAnimation(
  issueCount: number,
  animationDurationMs = 0
) {
  const lastUpdatedTimeStamp = useRef<number | null>(null)
  const [animate, setAnimate] = useState(false)

  useEffect(() => {
    if (issueCount > 0) {
      const deltaMs = lastUpdatedTimeStamp.current
        ? Date.now() - lastUpdatedTimeStamp.current
        : -1
      lastUpdatedTimeStamp.current = Date.now()

      // We don't animate if `issueCount` changes too q

es faster than the animation duration, it
      // will abruptly stop and not transition smoothly back to its original state.
      const timeoutId = window.setTimeout(() => {
        setAnimate(false)
      }, animationDurationMs)

      return () => {

uickly
      if (deltaMs <= animationDurationMs) {
        return
      }

      setAnimate(true)
      // It is important to use a CSS transitioned state, not a CSS keyframed animation
      // because if the issue count increas


ut: 'centered',
    docs: {
      description: {
        component:
          'A tooltip component built on @base-ui-components/react/tooltip. Supports 4 directions with configurable styling.',
      },
    },
  },
  argTypes: {
    direction: {
      control: { type: 'select' },
      options: ['top', 'bottom', 'left', 'right'],
      description: 'The direction where the tooltip should appear',
    },
    title: {
      control: { type: 'text' },
      description: 'The text content of the tooltip',
    }

iption: 'Distance between tooltip and trigger element',
    },
  },
}

export default meta
type Story = StoryObj<typeof Tooltip>

// Default story
export const Default: Story = {
  args: {
    title: 'This is a helpful tooltip',
    direction: 'top',
    a

,
    arrowSize: {
      control: { type: 'range', min: 2, max: 12, step: 1 },
      description: 'Size of the tooltip arrow in pixels',
    },
    offset: {
      control: { type: 'range', min: 0, max: 20, step: 1 },
      descr


 as BaseTooltip } from '@base-ui-components/react/tooltip'
import { useDevOverlayContext } from '../../../dev-overlay.browser'
import { cx } from '../../utils/cx'
import './tooltip.css'

type TooltipDirection = 'top' | 'bottom' | 'left' | 'right'

interface TooltipProps {
  children: React.ReactNode
  title: string | null
  direction?: TooltipDirection
  arrowSize?: number
  offset?: number
  className?: string
}

export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
  function Tooltip(
    {

eturn (
      <BaseTooltip.Provider>
        <BaseTooltip.Root delay={400}>
          <BaseTooltip.Trigger
            ref={ref}
            render={(triggerProps) => {
              return <span {...triggerProps}>{children}</span>
            }}

   className,
      children,
      title,
      direction = 'top',
      arrowSize = 6,
      offset = 8,
    },
    ref
  ) {
    const { shadowRoot } = useDevOverlayContext()
    if (!title) {
      return children
    }
    r


MPA page load
 *   [1, id, { from, to }] — captured SPA navigation (from/to route trees)
 *
 * The "to" tree may be null initially and updated after the prefetch resolves.
 */

import { useMemo } from 'react'
import { useSyncExternalStore } from 'react'
import type {
  FlightRouterState,
  Segment,
} from '../../../../shared/lib/app-router-types'
import {
  parseInstantNavCookieValue,
  type InstantNavCookieData,
} from '../../../../shared/lib/instant-nav-cookie'

const COOKIE_NAME = 'next-instant-navigatio

*)/)
  if (!match) return null
  return parseInstantNavCookieValue(match[1]).state
}

/**
 * Formats a FlightRouterState tree into a route pattern string for display.
 * Dynamic segments are shown with bracket syntax (e.g. [slug], [...params],
 * [[...opti

n-testing'

export function readInstantNavCookieState():
  | InstantNavCookieData['state']
  | null {
  if (typeof document === 'undefined') return null
  const match = document.cookie.match(/next-instant-navigation-testing=([^;]


rt './instant-navs-panel.css'
import type { CSSProperties, ReactNode } from 'react'
import type { InstantCookie } from '../../../../shared/lib/app-router-types'
import type { InstantNavCookieData } from '../../../../shared/lib/instant-nav-cookie'

const COOKIE_NAME = 'next-instant-navigation-testing'
type InstantNavContentStatus = 'idle' | 'pending' | 'mpa' | 'spa'
type InstantNavStatus =
  | InstantNavContentStatus
  // Waiting for the refresh action to start (renderingIndicator -> true).
  | 'rearming-awa

 event to land so the panel can read it.
  // Keeping the status non-idle through this window prevents a flicker
  // back to the idle UI between cookie write and cookie change event.
  | 'rearming-awaiting-cookie'

// Module-level state machine for the "C

iting-start'
  // Refresh in progress, waiting for it to finish (renderingIndicator -> false).
  | 'rearming-awaiting-end'
  // Refresh finished and the new pending cookie has been written; waiting
  // for the CookieStore change


utside } from '../../hooks/use-on-click-outside'

type DialogProps = {
  children?: React.ReactNode
  'aria-labelledby': string
  'aria-describedby': string
  className?: string
  onClose?: () => void
} & React.HTMLAttributes<HTMLDivElement>

const CSS_SELECTORS_TO_EXCLUDE_ON_CLICK_OUTSIDE = [
  '[data-next-mark]',
  '[data-issues-open]',
  '#nextjs-dev-tools-menu',
  '[data-nextjs-error-overlay-nav]',
  '[data-info-popover]',
  '[data-nextjs-devtools-panel-overlay]',
  '[data-nextjs-devtools-panel-footer]'

  const dialogRef = React.useRef<HTMLDivElement | null>(null)
  // TODO: Document is an external store. Either use useSyncExternalStore or always set the role.
  const [role, setRole] = React.useState<string | undefined>(
    typeof document !== 'undefined

,
  '[data-nextjs-error-overlay-footer]',
]

const Dialog: React.FC<DialogProps> = function Dialog({
  children,
  className,
  onClose,
  'aria-labelledby': ariaLabelledBy,
  'aria-describedby': ariaDescribedBy,
  ...props
}) {


 styles = css`
  [data-nextjs-dialog-root] {
    --next-dialog-radius: var(--rounded-xl);
    --next-dialog-max-width: 960px;
    --next-dialog-row-padding: 16px;
    --next-dialog-padding: 12px;
    --next-dialog-border-width: 1px;

    background-color: var(--color-gray-100);
    padding: 0 4px 4px 4px;
    border-radius: var(--rounded-2xl);
    display: flex;
    flex-direction: column;
    width: 100%;
    max-height: calc(100% - 56px);
    max-width: var(--next-dialog-max-width);
    margin-right: auto

true'] {
      opacity: 1;
      scale: 1;
    }
  }

  [data-nextjs-dialog] {
    outline: 0;
  }

  [data-nextjs-dialog-backdrop] {
    opacity: 0;
    transition: opacity var(--transition-duration) var(--timing-overlay);
  }

  [data-nextjs-dialog-overl

;
    margin-left: auto;
    scale: 0.97;
    opacity: 0;
    transition-property: scale, opacity;
    transition-duration: var(--transition-duration);
    transition-timing-function: var(--timing-overlay);

    &[data-rendered='


Obj } from '@storybook/react'
import { CallStackFrame } from './call-stack-frame'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof CallStackFrame> = {
  component: CallStackFrame,
  parameters: {
    layout: 'fullscreen',
    backgrounds: {
      default: 'background-100-dark',
    },
    a11y: {
      config: {
        rules: [
          {
            id: 'color-contrast',
            // Manual testing shows no violation.
            // TOD

allStackFrame>

const frame = {
  originalCodeFrame: null,
  originalStackFrame: {
    file: './app/page.tsx',
    methodName: 'MyComponent',
    arguments: [],
    line1: 10,
    column1: 5,
    ignored: false,
  },
  sourceStackFrame: {
    file: './app/

O: We might have setup more explicit backgrounds depending on theme.
            enabled: false,
          },
        ],
      },
    },
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof C


../hot-linked-text'
import { ExternalIcon, SourceMappingErrorIcon } from '../../icons/external'
import { getStackFrameFile } from '../../../shared/stack-frame'
import { useOpenInEditor } from '../../utils/use-open-in-editor'

export const CallStackFrame: React.FC<{
  frame: OriginalStackFrame
}> = function CallStackFrame({ frame }) {
  // TODO: ability to expand resolved frames

  const f = frame.originalStackFrame ?? frame.sourceStackFrame
  const hasOriginalCodeFrame = Boolean(frame.originalCodeFrame)
  c

 e.g. <anonymous> will be formatted to empty string,
  // we'll skip rendering the frame in this case.
  const stackFrameFile = getStackFrameFile(f)

  if (!stackFrameFile) {
    return null
  }

  return (
    <div
      data-nextjs-call-stack-frame

onst open = useOpenInEditor(
    hasOriginalCodeFrame
      ? {
          file: f.file,
          line1: f.line1 ?? 1,
          column1: f.column1 ?? 1,
        }
      : undefined
  )

  // Formatted file source could be empty.


arts } from '../../../../shared/lib/magic-identifier'

const linkRegex = /https?:\/\/[^\s/$.?#].[^\s)'"]*/i

export const HotlinkedText: React.FC<{
  text: string
  matcher?: (text: string) => string | null
}> = function HotlinkedText(props) {
  const { text, matcher } = props

  // Deobfuscate the entire text first
  const deobfuscatedParts = deobfuscateTextParts(text)

  return (
    <>
      {deobfuscatedParts.map(([type, part], outerIndex) => {
        if (type === 'raw') {
          return (

ink = linkRegex.exec(rawPart)!
                  const href = link[0]
                  // If link matcher is present, check if it returns a className
                  let linkClassName: string | null = null
                  if (typeof matcher === 'funct

  part
              // Split on whitespace and links
              .split(/(\s+|https?:\/\/[^\s/$.?#].[^\s)'"]*)/)
              .map((rawPart, index) => {
                if (linkRegex.test(rawPart)) {
                  const l


OpenInEditor } from '../../utils/use-open-in-editor'

type EditorLinkProps = {
  file: string
  isSourceFile: boolean
  location?: {
    line: number
    column: number
  }
}
export function EditorLink({ file, location }: EditorLinkProps) {
  const open = useOpenInEditor({
    file,
    line1: location?.line ?? 1,
    column1: location?.column ?? 1,
  })

  return (
    <div
      data-with-open-in-editor-link
      data-with-open-in-editor-link-import-trace
      role={'link'}
      onClick={open}
      ti

  stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      >
        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
        <polyline points="15 3 21 3 21 9"></polyline>

tle={'Click to open in your editor'}
    >
      {file}
      {location ? `:${location.line}:${location.column}` : null}
      <svg
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 24 24"
        fill="none"


l

  insert(urlPath: string): void {
    this._insert(urlPath.split('/').filter(Boolean), [], false)
  }

  smoosh(): string[] {
    return this._smoosh()
  }

  private _smoosh(prefix: string = '/'): string[] {
    const childrenPaths = [...this.children.keys()].sort()
    if (this.slugName !== null) {
      childrenPaths.splice(childrenPaths.indexOf('[]'), 1)
    }
    if (this.restSlugName !== null) {
      childrenPaths.splice(childrenPaths.indexOf('[...]'), 1)
    }
    if (this.optionalRestSlugName !=

, [])

    if (this.slugName !== null) {
      routes.push(
        ...this.children.get('[]')!._smoosh(`${prefix}[${this.slugName}]/`)
      )
    }

    if (!this.placeholder) {
      const r = prefix === '/' ? '/' : prefix.slice(0, -1)
      if (this.op

= null) {
      childrenPaths.splice(childrenPaths.indexOf('[[...]]'), 1)
    }

    const routes = childrenPaths
      .map((c) => this.children.get(c)!._smoosh(`${prefix}${c}/`))
      .reduce((prev, curr) => [...prev, ...curr]


erver/config-shared'
import { acceptLanguage } from '../../../server/accept-header'
import { denormalizePagePath } from '../page-path/denormalize-page-path'
import { detectDomainLocale } from './detect-domain-locale'
import { formatUrl } from '../router/utils/format-url'
import { getCookieParser } from '../../../server/api-utils/get-cookie-parser'

interface Options {
  defaultLocale: string
  domainLocale?: DomainLocale
  headers?: { [key: string]: string | string[] | undefined }
  nextConfig: {
    basePa

ring]: string | string[] | undefined } = {}
) {
  const nextLocale = getCookieParser(
    headers || {}
  )()?.NEXT_LOCALE?.toLowerCase()
  return nextLocale
    ? i18n.locales.find((locale) => nextLocale === locale.toLowerCase())
    : undefined
}

functi

th?: string
    i18n?: I18NConfig | null
    trailingSlash?: boolean
  }
  pathLocale?: string
  urlParsed: { hostname?: string | null; pathname: string }
}

function getLocaleFromCookie(
  i18n: I18NConfig,
  headers: { [key: st


port { BailoutToCSR } from './dynamic-bailout-to-csr'
import type { ComponentModule } from './types'
import { PreloadChunks } from './preload-chunks'

// Normalize loader to return the module as form { default: Component } for `React.lazy`.
// Also for backward compatible since next/dynamic allows to resolve a component directly with loader
// Client component reference proxy need to be converted to a module.
function convertModule<P>(
  mod: React.ComponentType<P> | ComponentModule<P> | undefined
): {
  de

fault: proxy(Component) }
  // mod: proxy(Component)
  const hasDefault = mod && 'default' in mod
  return {
    default: hasDefault
      ? (mod as ComponentModule<P>).default
      : (mod as React.ComponentType<P>),
  }
}

const defaultOptions = {
  load

fault: React.ComponentType<P>
} {
  // Check "default" prop before accessing it, as it could be client reference proxy that could break it reference.
  // Cases:
  // mod: { default: Component }
  // mod: Component
  // mod: { de


import { workAsyncStorage } from '../../../server/app-render/work-async-storage.external'
import { encodeURIPath } from '../encode-uri-path'
import { getAssetTokenQuery } from '../deployment-id'

export function PreloadChunks({
  moduleIds,
}: {
  moduleIds: string[] | undefined
}) {
  // Early return in client compilation and only load requestStore on server side
  if (typeof window !== 'undefined') {
    return null
  }

  const workStore = workAsyncStorage.getStore()
  if (workStore === undefined) {

 const manifest = workStore.reactLoadableManifest
    for (const key of moduleIds) {
      if (!manifest[key]) continue
      const chunks = manifest[key].files
      allFiles.push(...chunks)
    }
  }

  if (allFiles.length === 0) {
    return null
  }

return null
  }

  const allFiles = []

  // Search the current dynamic call unique key id in react loadable manifest,
  // and find the corresponding CSS files to preload
  if (workStore.reactLoadableManifest && moduleIds) {


NG_INDICATOR_SHOW,
  ACTION_RENDERING_INDICATOR_HIDE,
  ACTION_RENDERING_INDICATOR_SHOW,
  ACTION_DEVTOOL_UPDATE_ROUTE_STATE,
  ACTION_DEVTOOLS_CONFIG,
  type OverlayState,
  type DispatcherEvent,
  ACTION_CACHE_INDICATOR,
  ACTION_INSTANT_NAVS_TOGGLE,
} from './dev-overlay/shared'

import type { FlightRouterState } from '../shared/lib/app-router-types'
import {
  createContext,
  startTransition,
  useContext,
  useEffect,
  useInsertionEffect,
  useLayoutEffect,
  type ActionDispatch,
} from 'react'
impor

ation-error'
import type { DebugInfo } from './shared/types'
import { DevOverlay } from './dev-overlay/dev-overlay'
import type { DevIndicatorServerState } from '../server/dev/dev-indicator-server-state'
import type { VersionInfo } from '../server/dev/pars

t { createRoot } from 'react-dom/client'
import type { CacheIndicatorState } from './dev-overlay/cache-indicator'
import { FontStyles } from './dev-overlay/font/font-styles'
import type { HydrationErrorState } from './shared/hydr


 from './components/shadow-portal'
import { ComponentStyles } from './styles/component-styles'
import { ErrorOverlay } from './components/errors/error-overlay/error-overlay'
import { RenderError } from './container/runtime-error/render-error'
import { ScaleUpdater } from './styles/scale-updater'
import type { ReadyRuntimeError } from './utils/get-error-by-type'
import { DevToolsIndicator } from './components/devtools-indicator/devtools-indicator'
import { PanelRouter } from './menu/panel-router'
import { Pa

text = createContext<{
  runtimeErrors: ReadyRuntimeError[]
  totalErrorCount: number
}>(null!)

export const useRenderErrorContext = () => useContext(RenderErrorContext)

// Dispatches `ACTION_INSTANT_ERRORS_CLEAR` whenever the page changes to a
// new no

nelRouterContext, type PanelStateKind } from './menu/context'
import { useDevOverlayContext } from '../dev-overlay.browser'
import { ACTION_INSTANT_ERRORS_CLEAR, type DispatcherEvent } from './shared'

export const RenderErrorCon


ntNodeState } from '../userspace/app/segment-explorer-node'
import type * as SegmentExplorer from './segment-explorer-trie'

const createSegmentNode = ({
  pagePath,
  type,
}: {
  pagePath: string
  type: string
}): SegmentNodeState => {
  function placeholder() {}
  return {
    type,
    pagePath,
    boundaryType: null,
    setBoundaryType: placeholder,
  }
}

describe('Segment Explorer', () => {
  let cleanup: typeof import('@testing-library/react').cleanup
  let renderHook: typeof import('@testing-lib

(() => {
    jest.resetModules()
    jest.clearAllMocks()

    const segmentExplorer =
      require('./segment-explorer-trie') as typeof SegmentExplorer
    useSegmentTree = segmentExplorer.useSegmentTree
    insertSegmentNode = segmentExplorer.insertSegm

rary/react').renderHook
  let useSegmentTree: typeof SegmentExplorer.useSegmentTree
  let insertSegmentNode: typeof SegmentExplorer.insertSegmentNode
  let removeSegmentNode: typeof SegmentExplorer.removeSegmentNode

  beforeEach


outeRegex } from '../../shared/lib/router/utils/route-regex'

export type DevToolsConfig = {
  theme?: 'light' | 'dark' | 'system'
  disableDevIndicator?: boolean
  devToolsPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
  devToolsPanelPosition?: Record<
    string,
    'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
  >
  devToolsPanelSize?: Record<string, { width: number; height: number }>
  scale?: number
  hideShortcut?: string | null
}

export type Corners = 'top-left' |

}

export type DevToolsScale =
  (typeof NEXT_DEV_TOOLS_SCALE)[keyof typeof NEXT_DEV_TOOLS_SCALE]

type FastRefreshState =
  /** No refresh in progress. */
  | { type: 'idle' }
  /** The refresh process has been triggered, but the new code has not been ex

'top-right' | 'bottom-left' | 'bottom-right'
export type DevToolsIndicatorPosition = Corners

const BASE_SIZE = 16

export const NEXT_DEV_TOOLS_SCALE = {
  Small: BASE_SIZE / 14,
  Medium: BASE_SIZE / 16,
  Large: BASE_SIZE / 18,


rtionEffect } from 'react'

export const FontStyles = () => {
  useInsertionEffect(() => {
    const style = document.createElement('style')
    style.textContent = css`
      /* latin-ext */
      @font-face {
        font-family: '__nextjs-Geist';
        font-style: normal;
        font-weight: 400 600;
        font-display: swap;
        src: url(/__nextjs_font/geist-latin-ext.woff2) format('woff2');
        unicode-range:
          U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,

t Mono';
        font-style: normal;
        font-weight: 400 600;
        font-display: swap;
        src: url(/__nextjs_font/geist-mono-latin-ext.woff2) format('woff2');
        unicode-range:
          U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,


hShadowPortal } from '../../../../.storybook/decorators/with-shadow-portal'
import { lorem } from '../utils/lorem'
import {
  instantCurrentTimeErrors,
  instantClientMathRandomErrors,
  instantCryptoRandomUUIDErrors,
  instantMetadataErrors,
  instantMetadataUncachedErrors,
  instantMathRandomErrors,
  instantRuntimeDataErrors,
  instantUncachedDataErrors,
  instantViewportErrors,
  instantViewportUncachedErrors,
  runtimeErrors,
} from '../../../../.storybook/fixtures/errors'

const meta: Meta<typeof Erro

ders necessary, instead of passing props directly, before they expected props
export const Default: Story = {
  args: {
    getSquashedHydrationErrorDetails: () => null,
    runtimeErrors,
    versionInfo: {
      installed: '15.0.0',
      staleness: 'fre

rs> = {
  component: Errors,
  parameters: {
    layout: 'fullscreen',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof Errors>

// todo: update the stories to be wrapped in context provi


components/errors/error-overlay/error-overlay'
import type { HydrationErrorState } from '../../shared/hydration-error'
import { useActiveRuntimeError } from '../hooks/use-active-runtime-error'
import { formatCodeFrame } from '../components/code-frame/parse-code-frame'
import stripAnsi from 'next/dist/compiled/strip-ansi'
import {
  InstantHeaderExplanation,
  InstantGuidance,
  SYNC_IO_CLIENT_DOCS,
  SYNC_IO_DOCS,
  type GuidanceKind,
  type GuidanceVariant,
} from '../components/instant/instant-guidance'
i

overlay-call-stack/error-overlay-call-stack'
import { ErrorCause } from './runtime-error/error-cause'
import { useFrames } from '../utils/get-error-by-type'

interface ErrorsProps extends ErrorBaseProps {
  getSquashedHydrationErrorDetails: (error: Error)

mport { BLOCKING_ROUTE_NAVIGATION_EXPLANATION } from '../components/instant/instant-guidance-data'
import { CodeFrame } from '../components/code-frame/code-frame'
import { ErrorOverlayCallStack } from '../components/errors/error-


sh } from './ensure-leading-slash'
import { normalizePathSep } from './normalize-path-sep'
import path from '../isomorphic/path'
import { removePagePathTail } from './remove-page-path-tail'
import { normalizeMetadataRoute } from '../../../lib/metadata/get-metadata-route'
import type { PAGE_TYPES } from '../../../lib/page-types'

/**
 * Given the absolute path to the pages folder, an absolute file path for a
 * page and the page extensions, this function will return the page path
 * relative to the pages fol

/sitemap/route`
 *
 * @param filepath Absolute path to the page.
 * @param opts.dir Absolute path to the pages/app folder.
 * @param opts.extensions Extensions allowed for the page.
 * @param opts.keepIndex When true the trailing `index` kept in the path.

der. It doesn't consider index tail. Example:
 *  - `/Users/rick/my-project/pages/foo/bar/baz.js` -> `/foo/bar/baz`
 *
 * It also handles special metadata routes mapping. Example:
 * - `/Users/rick/my-project/app/sitemap.js` -> `


 a page and the trailing `index` if it exists
 * making sure to not return an empty string. The page head is not touched
 * and returned as it is passed. Examples:
 *   - `/foo/bar/baz/index.js` -> `/foo/bar/baz`
 *   - `/foo/bar/baz.js` -> `/foo/bar/baz`
 *
 * @param pagePath A page to a page file (absolute or relative)
 * @param options.extensions Extensions allowed for the page.
 * @param options.keepIndex When true the trailing `index` is _not_ removed.
 */
export function removePagePathTail(
  pagePath

<string>
    keepIndex?: boolean
  }
) {
  pagePath = normalizePathSep(pagePath).replace(
    new RegExp(`\\.+(?:${options.extensions.join('|')})$`),
    ''
  )

  if (options.keepIndex !== true) {
    pagePath = pagePath.replace(/\/index$/, '') || '/'
  }

: string,
  options: {
    extensions: ReadonlyArray


g]/[projectSlug]/monitoring',
      },
    ]

    const sorted = sortSortableRoutes(routes)

    expect(sorted).toEqual([
      {
        sourcePage: '/[lang]/[teamSlug]/~/monitoring',
        page: '/[lang]/[teamSlug]/~/monitoring',
      },
      {
        sourcePage: '/[lang]/[teamSlug]/[projectSlug]/monitoring',
        page: '/en/[teamSlug]/[projectSlug]/monitoring',
      },
      {
        sourcePage: '/[lang]/[teamSlug]/[projectSlug]/monitoring',
        page: '/fr/[teamSlug]/[projectSlug]/monitorin

ameter types', () => {
    const routes: SortableRoute[] = [
      {
        sourcePage: '/docs/[[...slug]]',
        page: '/docs/[[...slug]]',
      },
      {
        sourcePage: '/docs/[...slug]',
        page: '/docs/[...slug]',
      },
      {

g',
      },
      {
        sourcePage: '/[lang]/[teamSlug]/[projectSlug]/monitoring',
        page: '/[lang]/[teamSlug]/[projectSlug]/monitoring',
      },
    ])
  })

  it('should sort routes by specificity with different par


ple, the `app/[lang]/[...rest]/page.tsx` that was rendered with a lang
   * value of `en` would have a page of '/en/[...rest]'.
   */
  readonly page: string
}

/**
 * Determines the specificity of a route segment for sorting purposes.
 *
 * In Next.js routing, more specific routes should match before less specific ones.
 * This function returns a numeric value where lower numbers indicate higher specificity.
 *
 * Specificity order (most to least specific):
 * 1. Static segments (e.g., "about", "api") - re

ment (e.g., "api", "[id]", "[...slug]")
 * @returns A numeric specificity value (0-3, where 0 is most specific)
 */
export function getSegmentSpecificity(segment: string): number {
  // Static segments are most specific - they match exactly one path
  if (

turn 0
 * 2. Dynamic segments (e.g., "[id]", "[slug]") - return 1
 * 3. Catch-all segments (e.g., "[...slug]") - return 2
 * 4. Optional catch-all segments (e.g., "[[...slug]]") - return 3
 *
 * @param segment - A single path seg


/ This has to be a shared module which is shared between client component error boundary and dynamic component
const BAILOUT_TO_CSR = 'BAILOUT_TO_CLIENT_SIDE_RENDERING'

/** An error that should be thrown when we want to bail out to client-side rendering. */
export class BailoutToCSRError extends Error {
  public readonly digest = BAILOUT_TO_CSR

  constructor(public readonly reason: string) {
    super(`Bail out to client-side rendering: ${reason}`)
  }
}

/** Checks if a passed argument is an error that i

 want to bail out to client-side rendering. */
export function isBailoutToCSRError(err: unknown): err is BailoutToCSRError {
  if (typeof err !== 'object' || err === null || !('digest' in err)) {
    return false
  }

  return err.digest === BAILOUT_TO_CSR


x will have fast negatives meaning valid identifiers may not pass
// this test. However this is only used during static generation to provide hints
// about why a page bailed out of some or all prerendering and we can use bracket notation
// for example while `ಠ_ಠ` is a valid identifier it's ok to print `searchParams['ಠ_ಠ']`
// even if this would have been fine too `searchParams.ಠ_ಠ`
const isDefinitelyAValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/

export function describeStringPropertyAccess(target: strin

  prop: string
) {
  const stringifiedProp = JSON.stringify(prop)
  return `\`Reflect.has(${target}, ${stringifiedProp})\`, \`${stringifiedProp} in ${target}\`, or similar`
}

export const wellKnownProperties = new Set([
  'hasOwnProperty',
  'isPrototypeO

g, prop: string) {
  if (isDefinitelyAValidIdentifier.test(prop)) {
    return `\`${target}.${prop}\``
  }
  return `\`${target}[${JSON.stringify(prop)}]\``
}

export function describeHasCheckingStringProperty(
  target: string,


egment-explorer-node'

/**
 * Trie data structure for storing and searching paths
 *
 * This can be used to store app router paths and search for them efficiently.
 * e.g.
 *
 * [trie root]
 *   ├── layout.js
 *   ├── page.js
 *   ├── blog
 *       ├── layout.js
 *       ├── page.js
 *       ├── [slug]
 *          ├── layout.js
 *          ├── page.js
 **/

type TrieNode<Value = string> = {
  value: Value | undefined
  children: {
    [key: string]: TrieNode<Value> | undefined
  }
}

type Trie<Value = strin

> void
  getSnapshot: () => SegmentTrieNode
  getServerSnapshot: () => SegmentTrieNode
} => {
  // return a store that can be used by useSyncExternalStore
  return {
    subscribe: (callback) => {
      listeners.add(callback)
      return () => listeners.

g> = {
  insert: (value: Value) => void
  remove: (value: Value) => void
  getRoot: () => TrieNode<Value>
}

const listeners = new Set<() => void>()
const createSegmentTreeStore = (): {
  subscribe: (callback: () => void) => () =


{ Meta, StoryObj } from '@storybook/react'
import { BuildError } from './build-error'
import { withShadowPortal } from '../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof BuildError> = {
  component: BuildError,
  parameters: {
    layout: 'fullscreen',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof BuildError>

export const Default: Story = {
  args: {
    message: `./src/app/page.tsx:3:3
Lorem ipsum dolor sit amet, consectetur adipisc

const
> 3 |   return
Hello World

    |   ^^^^^^
  4 | }
  5 |

Expected identError: Failed to resolve import "./missing-module"

https://nextjs.org/docs/messages/module-not-found`,
    versionInfo: {
      installed: '15.0.0',
      staleness

ing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  1 | export default function Home() {
  2 |


wportError,
  createDynamicViewportError,
  createRuntimeBodyError,
  createRuntimeBodyErrorInNavigation,
  createRuntimeMetadataError,
  createRuntimeViewportError,
} from '../../../server/app-render/blocking-route-messages'
import {
  createSyncIOClientError,
  createSyncIOError,
  createSyncIORuntimeError,
  type SyncIOApiType,
} from '../../../server/app-render/sync-io-messages'
import {
  getBlockingRouteErrorDetails,
  isRuntimeVariant,
  isSyncIOClientError,
  isSyncIOError,
} from './errors'

const

namic body factory output', () => {
    expect(isRuntimeVariant(createDynamicBodyError(ROUTE).message)).toBe(false)
  })

  it('returns true for runtime metadata factory output', () => {
    expect(isRuntimeVariant(createRuntimeMetadataError(ROUTE).message

ROUTE = '/example'

describe('isRuntimeVariant', () => {
  it('returns true for runtime body factory output', () => {
    expect(isRuntimeVariant(createRuntimeBodyError(ROUTE).message)).toBe(true)
  })

  it('returns false for dy


import type { Meta, StoryObj } from '@storybook/react'
import { PseudoHtmlDiff } from './component-stack-pseudo-html'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof PseudoHtmlDiff> = {
  component: PseudoHtmlDiff,
  parameters: {
    layout: 'fullscreen',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof PseudoHtmlDiff>

export const TextMismatch: Story = {
  args: {
    reactOutputComponentDiff: `<

t>

-     Server content
+     Client content`,
  },
}

export const ReactUnifiedMismatch: Story = {
  args: {
    reactOutputComponentDiff:
      '<Page>\n  <Layout>\n
asd\n-
Server content
\n+
Client content
',
  },
}


import { addPathPrefix } from './add-path-prefix'
import { pathHasPrefix } from './path-has-prefix'

/**
 * For a given path and a locale, if the locale is given, it will prefix the
 * locale. The path shouldn't be an API path. If a default locale is given the
 * prefix will be omitted if the locale is already the default locale.
 */
export function addLocale(
  path: string,
  locale?: string | false,
  defaultLocale?: string,
  ignorePrefix?: boolean
) {
  // If no locale was given or the locale is the de

on't need to prefix the path.
  if (!ignorePrefix) {
    if (pathHasPrefix(lower, '/api')) return path
    if (pathHasPrefix(lower, `/${locale.toLowerCase()}`)) return path
  }

  // Add the locale prefix to the path.
  return addPathPrefix(path, `/${local

fault locale, we don't need
  // to prefix the path.
  if (!locale || locale === defaultLocale) return path

  const lower = path.toLowerCase()

  // If the path is an API path or the path already has the locale prefix, we
  // d


le {
  detectedLocale?: string
  pathname: string
}

/**
 * A cache of lowercased locales for each list of locales. This is stored as a
 * WeakMap so if the locales are garbage collected, the cache entry will be
 * removed as well.
 */
const cache = new WeakMap<readonly string[], readonly string[]>()

/**
 * For a pathname that may include a locale from a list of locales, it
 * removes the locale from the pathname returning it alongside with the
 * detected locale.
 *
 * @param pathname A pathname that may

 locales is undefined, return the pathname as is.
  if (!locales) return { pathname }

  // Get the cached lowercased locales or create a new cache entry.
  let lowercasedLocales = cache.get(locales)
  if (!lowercasedLocales) {
    lowercasedLocales = loca

include a locale.
 * @param locales A list of locales.
 * @returns The detected locale and pathname without locale
 */
export function normalizeLocalePath(
  pathname: string,
  locales?: readonly string[]
): PathLocale {
  // If


ng
 *
 * This module is shared between server and client.
 */

export type VaryParams = Set<string>

type FulfilledVaryParamsThenable = {
  status: 'fulfilled'
  value: VaryParams
} & PromiseLike<VaryParams>

type PendingVaryParamsThenable = {
  // 'resolved_model' is an internal React Flight state: the underlying model
  // data has arrived but the thenable hasn't been "unwrapped" yet. Calling
  // .then() triggers Flight to synchronously transition to 'fulfilled'.
  status: 'pending' | 'resolved_model'

(client-side or in collectSegmentData), the
 * thenable should already be fulfilled because the Flight stream has been
 * fully received. We check the status synchronously to avoid unnecessary
 * microtasks.
 *
 * Returns null if the thenable is still pend

value: unknown
} & PromiseLike<VaryParams>

export type VaryParamsThenable =
  | FulfilledVaryParamsThenable
  | PendingVaryParamsThenable

/**
 * Synchronously reads vary params from a thenable.
 *
 * By the time this is called


import stripAnsi from 'next/dist/compiled/strip-ansi'
import { Terminal } from '../components/terminal'
import { ErrorOverlayLayout } from '../components/errors/error-overlay-layout/error-overlay-layout'
import type { ErrorBaseProps } from '../components/errors/error-overlay/error-overlay'

interface BuildErrorProps extends ErrorBaseProps {
  message: string
}

const getErrorTextFromBuildErrorMessage = (multiLineMessage: string) => {
  const lines = multiLineMessage.split('\n')
  // The multi-line build er

.
  return (
    stripAnsi(lines[1] || '')
      // label will already say that it's an error
      .replace(/^Error: /, '')
  )
}

export const BuildError: React.FC<BuildErrorProps> = function BuildError({
  message,
  ...props
}) {
  const noop = useCall

ror message looks like:
  // <file path>:<line number>:<column number>
  // <error message>
  // <error code frame of compiler or bundler>
  // e.g.
  // ./path/to/file.js:1:1
  // SyntaxError: ...
  // > 1 | con st foo =
  // ..


 { denormalizePagePath } from './denormalize-page-path'
import path from '../isomorphic/path'

/**
 * Calculate all possible pagePaths for a given normalized pagePath along with
 * allowed extensions. This can be used to check which one of the files exists
 * and to debug inspected locations.
 *
 * For pages, map `/route` to [`/route.[ext]`, `/route/index.[ext]`]
 * For app paths, map `/route/page` to [`/route/page.[ext]`] or `/route/route`
 * to [`/route/route.[ext]`]
 *
 * @param normalizedPagePath Normal

malizedPagePath)

  let prefixes: string[]
  if (isAppDir) {
    prefixes = [page]
  } else if (normalizedPagePath.endsWith('/index')) {
    prefixes = [path.join(page, 'index')]
  } else {
    prefixes = [page, path.join(page, 'index')]
  }

  const paths

ized page path (it will denormalize).
 * @param extensions Allowed extensions.
 */
export function getPagePaths(
  normalizedPagePath: string,
  extensions: string[],
  isAppDir: boolean
) {
  const page = denormalizePagePath(nor


ct'
import type { OverlayState } from './shared'

// @ts-expect-error
import imgApp from './app.png'

import { DevOverlay } from './dev-overlay'
import { errors } from '../../../.storybook/fixtures/errors'
import {
  storybookDefaultOverlayState,
  useStorybookOverlayReducer,
} from '../../../.storybook/decorators/use-overlay-reducer'
import { DevOverlayContext, useDevOverlayContext } from '../dev-overlay.browser'

const meta: Meta<typeof DevOverlay> = {
  component: DevOverlay,
  parameters: {
    layout:

ng on theme.
            enabled: false,
          },
        ],
      },
    },
  },
}

export default meta
type Story = StoryObj<typeof DevOverlay>

function getNoSquashedHydrationErrorDetails() {
  return null
}

const initialState: OverlayState = {
  .

'fullscreen',
    a11y: {
      config: {
        rules: [
          {
            id: 'color-contrast',
            // Manual testing shows no violation.
            // TODO: We might have setup more explicit backgrounds dependi


 { ensureLeadingSlash } from './ensure-leading-slash'
import { isDynamicRoute } from '../router/utils'
import { NormalizeError } from '../utils'

/**
 * Takes a page and transforms it into its file counterpart ensuring that the
 * output is normalized. Note this function is not idempotent because a page
 * `/index` can be referencing `/index/index.js` and `/index/index` could be
 * referencing `/index/index/index.js`. Examples:
 *  - `/` -> `/index`
 *  - `/index/foo` -> `/index/index/foo`
 *  - `/index` ->

 ensureLeadingSlash(page)

  if (process.env.NEXT_RUNTIME !== 'edge') {
    const { posix } = require('path') as typeof import('path')
    const resolvedPage = posix.normalize(normalized)
    if (resolvedPage !== normalized) {
      throw new NormalizeErro

 `/index/index`
 */
export function normalizePagePath(page: string): string {
  const normalized =
    /^\/index(\/|$)/.test(page) && !isDynamicRoute(page)
      ? `/index${page}`
      : page === '/'
        ? '/index'
        :


reateRuntimeBodyErrorInNavigation,
} from '../../server/app-render/blocking-route-messages'
import { getInstantErrorRoute, routeTemplateMatchesPath } from './shared'

const STATIC_ROUTE = '/example'
const DYNAMIC_ROUTE_TEMPLATE = '/posts/[slug]'
const CATCH_ALL_ROUTE_TEMPLATE = '/docs/[...slug]'

describe('getInstantErrorRoute', () => {
  it('returns the route for an in-navigation runtime body error', () => {
    expect(
      getInstantErrorRoute(createRuntimeBodyErrorInNavigation(STATIC_ROUTE))
    ).toBe

ROUTE_TEMPLATE)
  })

  it('returns the route for the unrendered-segment wrapper', () => {
    const error = new Error(
      `Route "${STATIC_ROUTE}": Could not validate instant UI because an expected segment was not rendered.\n\nUnrendered segment:\n  ap

(STATIC_ROUTE)
  })

  it('returns the route for an in-navigation dynamic body error', () => {
    expect(
      getInstantErrorRoute(
        createDynamicBodyErrorInNavigation(DYNAMIC_ROUTE_TEMPLATE)
      )
    ).toBe(DYNAMIC_


Obj } from '@storybook/react'
import { ErrorMessage } from './error-message'
import { withShadowPortal } from '../../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof ErrorMessage> = {
  component: ErrorMessage,
  parameters: {
    layout: 'fullscreen',
    backgrounds: {
      default: 'background-100-light',
    },
    a11y: {
      config: {
        rules: [
          {
            id: 'color-contrast',
            // Manual testing shows no violation.
            // TODO: We

essage>

export const ShortString: Story = {
  args: {
    errorMessage: 'A simple error message',
  },
}

export const LongString: Story = {
  args: {
    errorMessage: `
    Lorem ipsum dolor sit amet consectetur.
    Aliquet nulla ut fames eu posuere le

 might have setup more explicit backgrounds depending on theme.
            enabled: false,
          },
        ],
      },
    },
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof ErrorM


./shared/stack-frame'
import { useMemo, useState, useRef } from 'react'
import { CallStack } from '../../call-stack/call-stack'

interface CallStackProps {
  frames: readonly OriginalStackFrame[]
  dialogResizerRef: React.RefObject<HTMLDivElement | null>
}

export function ErrorOverlayCallStack({
  frames,
  dialogResizerRef,
}: CallStackProps) {
  const initialDialogHeight = useRef<number>(NaN)
  const [isIgnoreListOpen, setIsIgnoreListOpen] = useState(false)

  const ignoredFramesTally = useMemo(() => {

ialog for animation
      if (!dialog) {
        // No dialog ref available, just close without animation
        setIsIgnoreListOpen(false)
        return
      }

      const { height: currentHeight } = dialog.getBoundingClientRect()

      if (!initialD

   return frames.reduce((tally, frame) => tally + (frame.ignored ? 1 : 0), 0)
  }, [frames])

  function onToggleIgnoreList() {
    const dialog = dialogResizerRef?.current

    if (isIgnoreListOpen) {
      // Closing requires d


mport { Dialog } from '../../dialog/dialog'

type ErrorOverlayDialogProps = {
  children?: React.ReactNode
  onClose?: () => void
} & React.HTMLAttributes<HTMLDivElement>

export function ErrorOverlayDialog({
  children,
  onClose,
  ...props
}: ErrorOverlayDialogProps) {
  return (
    <div className="error-overlay-dialog-container">
      <Dialog
        aria-labelledby="nextjs__container_errors_label"
        aria-describedby="nextjs__container_errors_desc"
        className="error-overlay-dialog-scroll"

ground: var(--color-background-100);
    background-clip: padding-box;
    border-radius: var(--next-dialog-radius);
    box-shadow: var(--shadow-menu);
    position: relative;
    overflow: hidden;
  }

  .error-overlay-dialog-scroll {
    overflow-y: aut

        onClose={onClose}
        {...props}
      >
        {children}
      </Dialog>
    </div>
  )
}

export const DIALOG_STYLES = `
  .error-overlay-dialog-container {
    display: flex;
    flex-direction: column;
    back


bject<HTMLElement | null>,
  triggerRef: React.RefObject<HTMLButtonElement | null> | null,
  active: boolean,
  onOpenFocus?: () => void
) {
  const fireOpenFocus = useEffectEvent((rootNode: HTMLElement | null) => {
    if (onOpenFocus) {
      onOpenFocus()
    } else {
      rootNode?.focus()
    }
  })
  useEffect(() => {
    let rootNode: HTMLElement | null = null

    function onTab(e: KeyboardEvent) {
      if (e.key !== 'Tab' || rootNode === null) {
        return
      }

      const [firstFocusable

()
          e.preventDefault()
        }
      } else {
        if (activeElement === lastFocusableNode) {
          firstFocusableNode?.focus()
          e.preventDefault()
        }
      }
    }

    const id = setTimeout(() => {
      // Grab this on

Node, lastFocusableNode] =
        getFocusableNodes(rootNode)
      const activeElement = getActiveElement(rootNode)

      if (e.shiftKey) {
        if (activeElement === firstFocusableNode) {
          lastFocusableNode?.focus


'react'

export function CacheDisabledBody(props: ComponentProps<'div'>) {
  return (

        While loading this page, all caches were bypassed.

        This is the case when the cache was disabled in the browser's devtools,
        the page was hard-reloaded, or draft mode is enabled.

 be the same as in
        production. React's DevTools will also not accurately show information
        about what would normally suspend in the page, and Next.js cannot
        validate whether a navigation to this page would be instant or blocking.

As a result, the loading experience might not


{ routerType: 'pages' | 'app' } & ComponentProps<'div'>) {
  return (

        The path{' '}
        {window.location.pathname}{' '}
        is marked as "static" since it will be prerendered during the build
        time.

        With Static Rendering, routes are rendered at build time, or in the

l-static-regeneration'
              : `https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration`
          }
          target="_blank"
          rel="noopener noreferrer"
        >
          data revalidation

     background after{' '}
        <a
          className="dev-tools-info-link"
          href={
            routerType === 'pages'
              ? 'https://nextjs.org/docs/pages/building-your-application/data-fetching/incrementa


Obj } from '@storybook/react'
import { ErrorOverlayCallStack } from './error-overlay-call-stack'
import { withShadowPortal } from '../../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof ErrorOverlayCallStack> = {
  component: ErrorOverlayCallStack,
  parameters: {
    layout: 'fullscreen',
    backgrounds: {
      default: 'background-100-dark',
    },
    a11y: {
      config: {
        rules: [
          {
            id: 'color-contrast',
            // Manual testing shows

a
type Story = StoryObj<typeof ErrorOverlayCallStack>

const frame = {
  originalStackFrame: {
    file: './app/page.tsx',
    methodName: 'MyComponent',
    arguments: [],
    line1: 10,
    column1: 5,
    ignored: false,
  },
  sourceStackFrame: {
    f

no violation.
            // TODO: We might have setup more explicit backgrounds depending on theme.
            enabled: false,
          },
        ],
      },
    },
  },
  decorators: [withShadowPortal],
}

export default met


rom '../hot-linked-text'
import { EditorLink } from './editor-link'
import { ExternalIcon } from '../../icons/external'
import { getStackFrameFile, type StackFrame } from '../../../shared/stack-frame'
import { useOpenInEditor } from '../../utils/use-open-in-editor'
import { FileIcon } from '../../icons/file'

type TerminalProps = { content: string }

function getFile(lines: string[]) {
  const contentFileName = lines.shift()
  if (!contentFileName) return null
  const [fileName, line, column] = contentFileN

ileName,
    location: hasLocation
      ? {
          line1: parsedLine,
          column1: parsedColumn,
        }
      : undefined,
  }
}

function getImportTraceFiles(lines: string[]) {
  if (
    lines.some((line) => /ReactServerComponentsError:/.tes

ame.split(':', 3)

  const parsedLine = Number(line)
  const parsedColumn = Number(column)
  const hasLocation = !Number.isNaN(parsedLine) && !Number.isNaN(parsedColumn)

  return {
    fileName: hasLocation ? fileName : contentF


 'error'
        error: unknown
      }
    | { state: 'success' }

  const [copyState, dispatch, isPending] = React.useActionState(
    (
      state: CopyState,
      action: 'reset' | 'copy'
    ): CopyState | Promise<CopyState> => {
      if (action === 'reset') {
        return { state: 'initial' }
      }
      if (action === 'copy') {
        if (!navigator.clipboard) {
          return {
            state: 'error',
            error: 'Copy to clipboard is not supported in this browser',
          }

       return { state: 'error', error }
            }
          )
        })
      }
      return state
    },
    {
      state: 'initial',
    }
  )

  function copy() {
    React.startTransition(() => {
      dispatch('copy')
    })
  }

  const reset =

        }
        return getContent().then((content) => {
          return navigator.clipboard.writeText(content).then(
            () => {
              return { state: 'success' }
            },
            (error) => {


Obj } from '@storybook/react'
import { CallStack } from './call-stack'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof CallStack> = {
  component: CallStack,
  parameters: {
    layout: 'fullscreen',
    backgrounds: {
      default: 'background-100-dark',
    },
    a11y: {
      config: {
        rules: [
          {
            id: 'color-contrast',
            // Manual testing shows no violation.
            // TODO: We might have setu

e = {
  originalStackFrame: {
    file: './app/page.tsx',
    methodName: 'MyComponent',
    arguments: [],
    line1: 10,
    column1: 5,
    ignored: false,
  },
  sourceStackFrame: {
    file: './app/page.tsx',
    methodName: 'MyComponent',
    argumen

p more explicit backgrounds depending on theme.
            enabled: false,
          },
        ],
      },
    },
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof CallStack>

const fram


tjs-dialog-overlay] {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    /* secondary z-index, -1 than toast z-index */
    z-index: 2147483646;

    display: flex;
    align-content: center;
    align-items: center;
    flex-direction: column;
    padding: 10vh 15px 0;
    /* color schemes we handle. Every other scheme the UA would need to overwrite */
    color-scheme: dark light;
  }

  @media (max-height: 812px) {
    [data-nextjs-dialog-overlay] {
      padding: 15px 15px 0;

sition: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: var(--color-backdrop);
    backdrop-filter: blur(10px);
    pointer-events: all;
    z-index: -1;
  }

  [data-nextjs-dialog-backdrop-fixed] {
    cursor: not-allowe

    }
  }

  [data-nextjs-dialog-backdrop] {
    po


' },
  dynamic: { label: 'Dynamic', color: 'blue', icon: 'server-stack' },
  client: { label: 'Client', color: 'amber', icon: 'layout' },
  defer: { label: 'Defer', color: 'amber', icon: 'pointer-click' },
  measure: { label: 'Measure', color: 'gray', icon: 'timer' },
}

export type FixCard = {
  /** Stable docs-anchor id for this fix card. */
  id: string
  title: string
  group: FixCardGroup
  /** Docs URL the card links to, or `null` for no link. */
  link: string | null
  snippets: Snippet[]
}

export t

ghlight` flag. `text` is still kept for any
  // tooling that reads the full line content.
  parts?: SnippetPart[]
}

// ── Blocking-route cards ──────────────────────────

const runtimeCards: FixCard[] = [
  {
    id: 'wrap-in-or-move-into-suspense',

ype SnippetPart = {
  text: string
  highlight?: boolean
}

export type Snippet = {
  text: string
  highlight?: boolean
  // When present, render the line with inline highlighted parts instead of
  // applying the line-level `hi


segment-explorer-path'
import { useClickOutsideAndEscape } from '../errors/dev-tools-indicator/utils'

const composeRefs = (...refs: (React.Ref<HTMLButtonElement> | undefined)[]) => {
  return (node: HTMLButtonElement | null) => {
    refs.forEach((ref) => {
      if (typeof ref === 'function') {
        ref(node)
      } else if (ref) {
        ref.current = node
      }
    })
  }
}

export function SegmentBoundaryTrigger({
  nodeState,
  boundaries,
}: {
  nodeState: SegmentNodeState
  boundaries: Record

vOverlayContext()
  const triggerRef = useRef<HTMLButtonElement>(null)
  const popupRef = useRef<HTMLDivElement>(null)

  // Click outside of popup should close the menu
  useClickOutsideAndEscape(
    popupRef,
    triggerRef,
    isOpen,
    () => {

<SegmentBoundaryType, string | null>
}) {
  const currNode = nodeState
  const { pagePath, boundaryType, setBoundaryType: onSelectBoundary } = currNode

  const [isOpen, setIsOpen] = useState(false)
  const { shadowRoot } = useDe


{ Meta, StoryObj } from '@storybook/react'
import { CodeFrame } from './code-frame'
import { withShadowPortal } from '../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof CodeFrame> = {
  component: CodeFrame,
  parameters: {
    layout: 'fullscreen',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof CodeFrame>

const baseStackFrame = {
  file: './app/page.tsx',
  methodName: 'Home',
  arguments: [],
  line1: 10,
  column1: 5,
}

export c

onst 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[


tils/use-open-in-editor'
import { ExternalIcon } from '../../icons/external'
import { FileIcon } from '../../icons/file'
import {
  formatCodeFrame,
  groupCodeFrameLines,
  parseLineNumberFromCodeFrameLine,
} from './parse-code-frame'

type CodeFrameProps = {
  stackFrame: StackFrame
  codeFrame: string
}

export function CodeFrame({ stackFrame, codeFrame }: CodeFrameProps) {
  const parsedLineStates = useMemo(() => {
    const decodedLines = groupCodeFrameLines(formatCodeFrame(codeFrame))

    return deco

e,
    line1: stackFrame.line1 ?? 1,
    column1: stackFrame.column1 ?? 1,
  })

  const fileExtension = stackFrame?.file?.split('.').pop()
  return (
    <div data-nextjs-codeframe>
      <div className="code-frame-header">
        {/* TODO: This is <div>

dedLines.map((line) => {
      return {
        line,
        parsedLine: parseLineNumberFromCodeFrameLine(line, stackFrame),
      }
    })
  }, [codeFrame, stackFrame])

  const open = useOpenInEditor({
    file: stackFrame.fil


ort type { Meta, StoryObj } from '@storybook/react'
import { ErrorOverlayLayout } from './error-overlay-layout'
import { withShadowPortal } from '../../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof ErrorOverlayLayout> = {
  component: ErrorOverlayLayout,
  parameters: {
    layout: 'fullscreen',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof ErrorOverlayLayout>

export const Default: Story = {
  args: {
    error: {
      name: 'Mo

aleness: 'fresh',
    },
    children: (
      <div style={{ margin: '1rem' }}>
        Module not found: Cannot find module './missing-module'
      </div>
    ),
  },
}

export const Turbopack: Story = {
  args: {
    ...Default.args,
    isTurbopack: tr

duleNotFoundError',
      message: "Cannot find module './missing-module'",
    },
    errorType: 'Build Error',
    errorMessage: 'Failed to compile',
    errorCode: 'E001',
    versionInfo: {
      installed: '15.0.0',
      st


} from './error-feedback/error-feedback'
import { styles as feedbackStyles } from './error-feedback/error-feedback'

type ErrorOverlayFooterProps = {
  errorCode: string | undefined
}

export function ErrorOverlayFooter({ errorCode }: ErrorOverlayFooterProps) {
  return (
    <footer data-nextjs-error-overlay-footer className="error-overlay-footer">
      {errorCode ? (
        <ErrorFeedback className="error-feedback" errorCode={errorCode} />
      ) : null}
    </footer>
  )
}

export const styles = `
  .

irection: row;
    justify-content: space-between;

    gap: 8px;
    padding: 12px 12px 8px 12px;
  }

  .error-feedback {
    margin-left: auto;

    p {
      font-size: var(--size-14);
      font-weight: 500;
      margin: 0;
    }
  }

  ${feedbackSty

error-overlay-footer {
    display: flex;
    flex-d


e-label/error-type-label'

export type ErrorMessageType = React.ReactNode

type ErrorMessageProps = {
  errorMessage: ErrorMessageType
  errorType: ErrorType
}

export function ErrorMessage({ errorMessage, errorType }: ErrorMessageProps) {
  const [isExpanded, setIsExpanded] = useState(false)
  const [isTooTall, setIsTooTall] = useState(false)
  const messageRef = useRef<HTMLDivElement>(null)

  useLayoutEffect(() => {
    if (messageRef.current) {
      setIsTooTall(messageRef.current.scrollHeight > 200)

 scroll overflow instead.
  const shouldTruncate = isTooTall && errorType !== 'Instant'

  return (
    <>
      <div className="nextjs__container_errors_wrapper">
        <div
          ref={messageRef}
          id="nextjs__container_errors_desc"

   }
  }, [errorMessage])

  if (!errorMessage) {
    return null
  }

  // Instant errors are formatted specifically for the overlay rather than
  // passed through from the console, so we don't truncate them — they rely
  // on


le,
  normalizeBoundaryFilename,
} from '../../../../server/app-render/segment-explorer-path'
import { SegmentSuggestion } from './segment-suggestion'
import type { SegmentBoundaryType } from '../../../userspace/app/segment-explorer-node'

const isFileNode = (node: SegmentTrieNode) => {
  return !!node.value?.type && !!node.value?.pagePath
}

// Utility functions for global boundary management
function traverseTreeAndResetBoundaries(node: SegmentTrieNode) {
  // Reset this node's boundary if it has setBound

ndaries(child)
    }
  })
}

function countActiveBoundaries(node: SegmentTrieNode): number {
  let count = 0

  // Count this node's boundary override if it's active
  // Only count when there's a non ":boundary" type and it has an active override (boundar

aryType function
  if (node.value?.setBoundaryType) {
    node.value.setBoundaryType(null)
  }

  // Recursively traverse children
  Object.values(node.children).forEach((child) => {
    if (child) {
      traverseTreeAndResetBou


rentColor',
  strokeWidth: 2,
  strokeLinecap: 'round' as const,
  strokeLinejoin: 'round' as const,
}

export function FixCardAlignLeftIcon() {
  return (
    <svg {...ICON_PROPS}>
      <path d="M3 6h18M3 12h12M3 18h18" />
    </svg>
  )
}

export function FixCardServerStackIcon() {
  return (
    <svg
      width="16"
      height="16"
      viewBox="0 0 16 16"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        fill="currentColor"
        fillRule="evenodd"
        clipR


 type { ComponentProps } from 'react'

export function ThumbsUp(props: ComponentProps<'svg'>) {
  return (
    <svg
      width="16"
      height="16"
      viewBox="0 0 16 16"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      className="thumbs-up-icon"
      {...props}
    >
      <g id="thumb-up-16">
        <path
          id="Union"
          fillRule="evenodd"
          clipRule="evenodd"
          d="M6.89531 2.23959C6.72984 2.1214 6.5 2.23968 6.5 2.44303V5.24989C6.5 6.21639 5.7165 6.99


s'

describe('css template literal tag', () => {
  it('should combines strings and interpolations correctly', () => {
    const color = 'red'
    const result = css`
      color: ${color};
      background: blue;
    `
    expect(result).toBe('color:red;background:blue;')
  })

  it('should remove all whitespace', () => {
    const result = css`
      margin: 0;
      padding: 10px;
    `
    expect(result).toBe('margin:0;padding:10px;')
  })

  it('should remove CSS comments', () => {
    const result = cs

() => {
    const result = css`
      color: blue;
      /*
       * This is multi-line comment.
       * Is should be removed.
       */
      margin: 10px;
    `
    expect(result).toBe('color:blue;margin:10px;')
  })

  it('should handle multiple interp

s`
      color: blue; /* this is a comment */
      /* multi-line
         comment */
      /** jsdoc */
      margin: 10px;
    `
    expect(result).toBe('color:blue;margin:10px;')
  })

  it('should remove multiline comments',


tack-frame'
import { getErrorSource } from '../../../shared/lib/error-source'
import { parseStack } from '../../../server/lib/parse-stack'
import React from 'react'

export type ReadyErrorCause =
  | {
      error: Error
      frames: () => Promise<readonly OriginalStackFrame[]>
      cause?: ReadyErrorCause
    }
  | {
      error: AggregateError
      frames: () => Promise<readonly OriginalStackFrame[]>
      cause?: ReadyErrorCause
      aggregateErrors: ReadyErrorCause[] | null
    }

export type ReadyR

 ReadyErrorCause
    }
  | {
      id: number
      runtime: true
      error: AggregateError & { environmentName?: string }
      frames: () => Promise<readonly OriginalStackFrame[]>
      type: 'runtime' | 'console' | 'recoverable'
      cause?: ReadyErr

untimeError =
  | {
      id: number
      runtime: true
      error: Error & { environmentName?: string }
      frames: () => Promise<readonly OriginalStackFrame[]>
      type: 'runtime' | 'console' | 'recoverable'
      cause?:


nte non
malesuada. Nunc congue urna id laoreet tempor. Nunc rhoncus nec ante eu dapibus. Sed egestas neque vel
vehicula ullamcorper. Phasellus quis accumsan turpis, et condimentum nibh. Sed dignissim diam ex, consectetur
aliquet lectus feugiat a. Etiam tempor varius massa, sed suscipit nulla. Pellentesque orci est, porta a massa
ac, pretium pulvinar mi. Praesent ac placerat leo, tempus hendrerit ipsum. Phasellus felis ex, blandit et
nibh at, tincidunt gravida est.

In non orci vel libero sollicitudin o

pibus et. Donec sodales dui in semper consequat.
Suspendisse potenti. Aliquam augue dolor, tincidunt quis sem a, rhoncus porttitor eros. Sed molestie leo
eget elementum vehicula.

Praesent velit turpis, blandit non ex et, lacinia elementum dolor. Aliquam

rnare. Nunc quis augue id velit venenatis posuere. Aenean et velit
fermentum, facilisis nunc vel, cursus purus. Integer tempus velit et diam vulputate, in convallis nisi
feugiat. Sed euismod aliquet neque, ac fermentum risus da


seUrlFromText } from './parse-url-from-text'

describe('parseUrlFromText', () => {
  it('should extract a URL from text', () => {
    const text = 'Check out https://nextjs.org for more info'
    expect(parseUrlFromText(text)).toEqual(['https://nextjs.org'])
  })

  it('should extract multiple URLs from text', () => {
    const text = 'Visit https://react.dev and https://vercel.com'
    expect(parseUrlFromText(text)).toEqual([
      'https://react.dev',
      'https://vercel.com',
    ])
  })

  it('should

tarted?query=123#fragment',
    ])
  })

  it('should return empty array when no URLs are found', () => {
    const text = 'This text contains no URLs'
    expect(parseUrlFromText(text)).toEqual([])
  })

  it('should handle empty string input', () => {

handle URLs with paths and query parameters', () => {
    const text =
      'Link: https://nextjs.org/docs/getting-started?query=123#fragment'
    expect(parseUrlFromText(text)).toEqual([
      'https://nextjs.org/docs/getting-s


 type { DevToolsConfig } from '../shared'
import { devToolsConfigSchema } from '../../shared/devtools-config-schema'
import { deepMerge } from '../../shared/deepmerge'

let queuedConfigPatch: DevToolsConfig = {}
let timer: ReturnType<typeof setTimeout> | null = null

function flushPatch() {
  if (Object.keys(queuedConfigPatch).length === 0) {
    return
  }

  const body = JSON.stringify(queuedConfigPatch)
  queuedConfigPatch = {}

  fetch('/__nextjs_devtools_config', {
    method: 'POST',
    headers: { 'C

     data: body,
      error,
    })
  })
}

export function saveDevToolsConfig(patch: DevToolsConfig) {
  const validation = devToolsConfigSchema.safeParse(patch)
  if (!validation.success) {
    console.warn(
      '[Next.js DevTools] Invalid config patc

ontent-Type': 'application/json' },
    body,
    // keepalive in case of fetch interrupted, e.g. navigation or reload
    keepalive: true,
  }).catch((error) => {
    console.warn('[Next.js DevTools] Failed to save config:', {


ction useOpenInEditor({
  file,
  line1,
  column1,
}: {
  file?: string | null
  line1?: number | null
  column1?: number | null
} = {}) {
  const openInEditor = useCallback(() => {
    if (file == null || line1 == null || column1 == null) return

    const params = new URLSearchParams()
    params.append('file', file)
    params.append('line1', String(line1))
    params.append('column1', String(column1))

    self
      .fetch(
        `${
          process.env.__NEXT_ROUTER_BASEPATH || ''
        }/__nex

  .then(
        () => {},
        (cause) => {
          console.error(
            `Failed to open file "${file} (${line1}:${column1})" in your editor. Cause:`,
            cause
          )
        }
      )
  }, [file, line1, column1])

  return openIn

tjs_launch-editor?${params.toString()}`
      )


de-frame/code-frame'
import { styles as dialog } from '../components/dialog'
import { styles as errorLayout } from '../components/errors/error-overlay-layout/error-overlay-layout'
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 { TERMINAL_STYLES } from '../components/terminal/ter

'../container/errors'
import { styles as containerRuntimeErrorStyles } from '../container/runtime-error'
import { COPY_BUTTON_STYLES } from '../components/copy-button'
import { CALL_STACK_FRAME_STYLES } from '../components/call-stack-frame/call-stack-frame

minal'
import { styles as versionStaleness } from '../components/version-staleness-info/version-staleness-info'
import { styles as buildErrorStyles } from '../container/build-error'
import { styles as containerErrorStyles } from


wardRef, useEffect, useState } from 'react'

export const Resizer = forwardRef(function Resizer(
  {
    children,
    measure,
    ...props
  }: {
    children: React.ReactNode
    measure: boolean
  } & React.HTMLProps<HTMLDivElement>,
  resizerRef: React.Ref<HTMLDivElement | null>
) {
  const [element, setElement] = useState<HTMLDivElement | null>(null)
  const [height, measuring] = useMeasureHeight(element, measure)

  return (
    <div
      {...props}
      ref={resizerRef}
      // [x] Don't animate

div ref={setElement}>{children}</div>
    </div>
  )
})

function useMeasureHeight(
  element: HTMLDivElement | null,
  measure: boolean
): [number, boolean] {
  const [height, setHeight] = useState<number>(0)
  const [measuring, setMeasuring] = useState<b

on initial load
      // [x] No duplicate elements
      // [x] Responds to content growth
      style={{
        height: measuring ? 'auto' : height,
        transition: 'height 250ms var(--timing-swift)',
      }}
    >
      <


ayDispatch,
  type OverlayState,
} from '../../../shared'

import { Activity } from 'react'
import { BuildError } from '../../../container/build-error'
import { Errors } from '../../../container/errors'
import { useDelayedRender } from '../../../hooks/use-delayed-render'
import type { ReadyRuntimeError } from '../../../utils/get-error-by-type'
import type { HydrationErrorState } from '../../../../shared/hydration-error'

const transitionDurationMs = 200

export interface ErrorBaseProps {
  rendered: boolean

errorCount,
}: {
  state: OverlayState
  dispatch: OverlayDispatch
  getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null
  runtimeErrors: ReadyRuntimeError[]
  errorCount: number
}) {
  const isTurbopack = !!process.env.TURBOPACK

  transitionDurationMs: number
  isTurbopack: boolean
  versionInfo: OverlayState['versionInfo']
  errorCount: number
}

export function ErrorOverlay({
  state,
  dispatch,
  getSquashedHydrationErrorDetails,
  runtimeErrors,


t type { Meta, StoryObj } from '@storybook/react'
import { ErrorOverlayToolbar } from './error-overlay-toolbar'
import { withShadowPortal } from '../../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof ErrorOverlayToolbar> = {
  component: ErrorOverlayToolbar,
  parameters: {
    layout: 'centered',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof ErrorOverlayToolbar>

export const WithErrorAndDebugInfo: Story = {
  args: {
    error: ne

error with stack trace'),
    debugInfo: {
      devtoolsFrontendUrl: 'chrome-devtools://devtools/bundled/inspector.html',
    },
  },
}

export const WithErrorOnly: Story = {
  args: {
    error: new Error('Test error without debug info'),
    debugInfo:


   fill="url(#nodejs_icon_linear_gradient_b)"
        />
      </g>
      <mask
        id="nodejs_icon_mask_c"
        style={{ maskType: 'luminance' }}
        maskUnits="userSpaceOnUse"
        x="1"
        y="0"
        widt


seRestartServer() {
  const [isPending, setIsPending] = useState(false)

  const restartServer = async ({
    invalidateFileSystemCache,
  }: {
    invalidateFileSystemCache: boolean
  }): Promise<void> => {
    setIsPending(true)

    const url = invalidateFileSystemCache
      ? '/__nextjs_restart_dev?invalidateFileSystemCache=1'
      : '/__nextjs_restart_dev'

    let serverRestarted = false

    try {
      const curId = await fetch('/__nextjs_server_status')
        .then((res) => res.json())

eturn null
        })

      if (!curId) {
        console.log(
          '[Next.js DevTools] Failed to get the current server execution ID while restarting dev server.'
        )
        return
      }

      const restartRes = await fetch(url, {

.then((data) => data.executionId as number)
        .catch((error) => {
          console.log(
            '[Next.js DevTools] Failed to fetch server status while restarting dev server.',
            error
          )
          r


{ Meta, StoryObj } from '@storybook/react'
import { ErrorOverlayNav } from './error-overlay-nav'
import { withShadowPortal } from '../../../../../../.storybook/decorators/with-shadow-portal'

const meta: Meta<typeof ErrorOverlayNav> = {
  component: ErrorOverlayNav,
  parameters: {
    layout: 'fullscreen',
  },
  decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof ErrorOverlayNav>

export const Default: Story = {
  args: {
    runtimeErrors: [
      {
        id: 0,

sage'),
        frames: () => Promise.resolve([]),
        type: 'runtime',
      },
      {
        id: 2,
        runtime: true,
        error: new Error('Third error message'),
        frames: () => Promise.resolve([]),
        type: 'runtime',
      },

runtime: true,
        error: new Error('First error message'),
        frames: () => Promise.resolve([]),
        type: 'runtime',
      },
      {
        id: 1,
        runtime: true,
        error: new Error('Second error mes


 } from '../components/devtools-indicator/devtools-indicator'
import { usePanelRouterContext } from './context'
import { usePanelContext } from './panel-router'

interface C {
  closeMenu?: () => void
  selectedIndex: number
  setSelectedIndex: Dispatch<SetStateAction<number>>
}

const MenuContext = createContext({} as C)

function MenuItem({
  index,
  label,
  value,
  onClick,
  href,
  ...props
}: {
  index?: number
  title?: string
  label: string
  value: React.ReactNode
  href?: string
  onClick?: ()

nction click() {
    if (isInteractive) {
      onClick?.()
      closeMenu?.()
      if (href) {
        window.open(href, '_blank', 'noopener, noreferrer')
      }
    }
  }

  return (
    <div
      className="dev-tools-indicator-item"
      data-index

 => void
}) {
  const isInteractive =
    typeof onClick === 'function' || typeof href === 'string'
  const { closeMenu, selectedIndex, setSelectedIndex } = useContext(MenuContext)
  const selected = selectedIndex === index

  fu


sHeader } from '../components/errors/dev-tools-indicator/dev-tools-info/dev-tools-header'
import { useDelayedRender } from '../hooks/use-delayed-render'
import {
  MENU_CURVE,
  MENU_DURATION_MS,
} from '../components/errors/dev-tools-indicator/utils'
import { useDevOverlayContext } from '../../dev-overlay.browser'
import { createContext, useContext } from 'react'
import { useRenderErrorContext } from '../dev-overlay'
import {
  ACTION_DEV_INDICATOR_SET,
  ACTION_DEVTOOLS_POSITION,
  ACTION_DEVTOOLS_SCALE,

/dev-tools-indicator/dev-tools-info/user-preferences'
import { useShortcuts } from '../hooks/use-shortcuts'
import { useUpdateAllPanelPositions } from '../components/devtools-indicator/devtools-indicator'
import { saveDevToolsConfig } from '../utils/save-d

  ACTION_ERROR_OVERLAY_CLOSE,
  ACTION_ERROR_OVERLAY_OPEN,
} from '../shared'
import GearIcon from '../icons/gear-icon'
import { LoadingIcon } from '../icons/loading-icon'
import { UserPreferencesBody } from '../components/errors