Repository: vercel/next.js
Pull request: [segment explorer] fix boundary trigger state

* Mark boundary trigger as disabled when there's no boundary
* Fix the styling of icon, it should not show a round button wrapping it

Closes NEXT-4616
Closes NEXT-4617

Changed files:
filename: packages/next/src/next-devtools/dev-overlay/components/overview/segment-boundary-trigger.css
patch: @@ -47,15 +47,6 @@
   cursor: not-allowed;
 }
 
-.segment-boundary-trigger--boundary {
-  background: var(--color-background-100);
-  box-shadow: inset 0 0 0 1px var(--color-gray-400);
-}
-
-.segment-boundary-trigger--boundary:hover {
-  background: var(--color-gray-200);
-}
-
 .segment-boundary-dropdown {
   padding: 8px;
   background: var(--color-background-100);

filename: packages/next/src/next-devtools/dev-overlay/components/overview/segment-boundary-trigger.tsx
patch: @@ -2,11 +2,7 @@ import './segment-boundary-trigger.css'
 import { useCallback, useState, useRef, useMemo } from 'react'
 import { Menu } from '@base-ui-components/react/menu'
 import type { SegmentNodeState } from '../../../userspace/app/segment-explorer-node'
-import {
-  isBoundaryFile,
-  normalizeBoundaryFilename,
-} from '../../../../server/app-render/segment-explorer-path'
-import { cx } from '../../utils/cx'
+import { normalizeBoundaryFilename } from '../../../../server/app-render/segment-explorer-path'
 import { useClickOutside } from '../errors/dev-tools-indicator/utils'
 
 const composeRefs = (...refs: (React.Ref<HTMLButtonElement> | undefined)[]) => {
@@ -29,13 +25,7 @@ export function SegmentBoundaryTrigger({
   boundaries: Record<'not-found' | 'loading' | 'error', string | null>
 }) {
   const currNode = nodeState
-  const {
-    pagePath,
-    boundaryType,
-    type,
-    setBoundaryType: onSelectBoundary,
-  } = currNode
-  const fileType = type
+  const { pagePath, boundaryType, setBoundaryType: onSelectBoundary } = currNode
 
   const [isOpen, setIsOpen] = useState(false)
   // TODO: move this shadowRoot ref util to a shared hook or into context
@@ -76,15 +66,12 @@ export function SegmentBoundaryTrigger({
   }, [boundaries, possibleExtension])
 
   const fileName = (pagePath || '').split('/').pop() || ''
-  const isBoundary = isBoundaryFile(fileType)
   const pageFileName = normalizeBoundaryFilename(
     boundaryType
       ? `page.${possibleExtension}`
       : fileName || `page.${possibleExtension}`
   )
 
-  const isPageOrBoundary = fileType && !isBoundary
-
   const triggerOptions = [
     {
       label: fileNames.loading,
@@ -158,15 +145,21 @@ export function SegmentBoundaryTrigger({
     return <Trigger {...triggerProps} ref={mergedRef} />
   }
 
+  const hasBoundary = useMemo(() => {
+    const hasPageOrBoundary =
+      nodeState.type !== 'layout' && nodeState.type !== 'template'
+    return (
+      hasPageOrBoundary && Object.values(boundaries).some((v) => v !== null)
+    )
+  }, [nodeState.type, boundaries])
+
   return (
     <Menu.Root delay={0} modal={false} open={isOpen} onOpenChange={setIsOpen}>
       <Menu.Trigger
-        className={cx(
-          'segment-boundary-trigger',
-          !isPageOrBoundary && 'segment-boundary-trigger--boundary'
-        )}
+        className="segment-boundary-trigger"
         data-nextjs-dev-overlay-segment-boundary-trigger-button
         render={MergedRefTrigger}
+        disabled={!hasBoundary}
       />
 
       {/* @ts-expect-error remove this expect-error once shadowRoot is supported as container */}
@@ -330,13 +323,9 @@ function SwitchIcon(props: React.SVGProps<SVGSVGElement>) {
   )
 }
 
-export function Trigger(props: React.ComponentProps<'button'>) {
+function Trigger(props: React.ComponentProps<'button'>) {
   return (
-    <button
-      {...props}
-      className={cx('segment-boundary-trigger', props.className)}
-      role="button"
-    >
+    <button {...props}>
       <span className="segment-boundary-trigger-text">
         <SwitchIcon className="plus-icon" />
       </span>

filename: packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx
patch: @@ -4,7 +4,7 @@ import {
   type SegmentTrieNode,
 } from '../../segment-explorer-trie'
 import { cx } from '../../utils/cx'
-import { SegmentBoundaryTrigger, Trigger } from './segment-boundary-trigger'
+import { SegmentBoundaryTrigger } from './segment-boundary-trigger'
 import { Tooltip } from '../../../components/tooltip'
 import { useCallback, useMemo } from 'react'
 import {
@@ -257,12 +257,6 @@ function PageSegmentTreeLayerPresentation({
 
   const filesChildrenKeysBesidesSelectedBoundary = filesChildrenKeys
 
-  const isChildPageOrBoundary =
-    firstChild &&
-    firstChild.value &&
-    firstChild.value.type !== 'layout' &&
-    firstChild.value.type !== 'template'
-
   return (
     <>
       {hasFilesChildren && (
@@ -359,13 +353,12 @@ function PageSegmentTreeLayerPresentation({
                   )}
                 </span>
               )}
-              {firstChild && firstChild.value && isChildPageOrBoundary && (
+              {firstChild && firstChild.value && (
                 <SegmentBoundaryTrigger
                   nodeState={firstChild.value}
                   boundaries={boundaries}
                 />
               )}
-              {!isChildPageOrBoundary && <Trigger disabled />}
             </div>
           </div>
         </div>