// Hero chrome for the blank-draft phase of ConversationRoot: fish headline, // glow backdrop, and the workspace row. Pure presentation — the resident // composer is NOT rendered here (it keeps its own stable tree position in // ConversationRoot so the textarea survives the hero → composer flip); CSS // positions it over this shell's glow area during the hero phase. import { useId } from 'react' import type { ReactNode, RefObject } from 'react' import { FishLogo, IconChevronDownOutline14, IconFolderClose16, IconFolderOpen16, } from '@deepseek-ai/dsh-client-ui-primitives' import { workspaceTitleOf } from '@deepseek-ai/dsh-client-runtime/client' import type { ConversationSlotProps } from '../contract/slots.ts' import css from './HeroShell.module.css' /** The owner's locale seat type, passed to hero chrome as a plain prop. */ type HeroTranslate = ConversationSlotProps['t'] /** * Basename label for the workspace chip (the shared derivation); * separator-only paths echo the raw cwd. * @param cwd - workspace directory path (non-empty). * @returns chip label. */ export function workspaceLabel(cwd: string): string { const base = workspaceTitleOf(cwd) return base !== '' ? base : cwd } /** * The workspace chip (folder + label + chevron), always interactive: before * the first message the workspace stays switchable — picking another one * moves the New Session flow to that workspace's blank session. Without a * label the chip renders its placeholder state: closed folder + the * "Choose workspace" call to action. * @param props.label - chip label (see {@link workspaceLabel}); omitted → placeholder. * @param props.menuOpen - menu expansion echo. * @param props.onClick - menu toggle. * @returns the chip button element. */ export function WorkspaceChip({ buttonRef, label, menuOpen = false, onClick, t }: { buttonRef?: RefObject label?: string | undefined menuOpen?: boolean onClick?: () => void t: HeroTranslate }) { return ( ) } /** * The soft blue backdrop ellipse (figma 313:14109). Rendered by the hero * owner (ConversationRoot), not HeroShell, so it can center on the input * card; the owner's className supplies all positioning. * @param props.className - positioning class from the owner. * @returns the blurred-ellipse svg element. */ export function HeroGlow({ className }: { className?: string | undefined }) { // Stable filter id so multiple hero mounts do not collide in the DOM. const glowFilterId = `empty-glow-${useId().replace(/:/g, '')}` return ( ) } /** Hero chrome props. The workspace row rides the InputBar accessory hole, not here. */ export interface HeroShellProps { /** The owner's locale seat, passed down as a plain prop. */ t: HeroTranslate /** Overlay content after the stack (modals). */ children?: ReactNode } /** * Render the hero chrome (headline only; no glow, no composer, no workspace * row — the glow is the owner's {@link HeroGlow}). * @param props - see {@link HeroShellProps}. * @returns the centered hero element tree. */ export function HeroShell({ t, children }: HeroShellProps) { return (
{/* figma 34:10412: fish 34×25 leading the headline, gap 10. */} {t('hero.headline')} {t('hero.preview')}
{/* The resident composer (ConversationRoot wrapActiveBody seat; the workspace row rides the stack above the card) is CSS-centered in the session scroll body during hero — see ConversationRoot.module.css [data-phase='hero']. */}
{children}
) }