{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-message",
  "type": "registry:component",
  "title": "Message",
  "description": "A typical agent conversation",
  "author": "sudo-kw <https://github.com/sudo-kw>",
  "dependencies": [
    "@gpuix/native@0.11.0",
    "@gpuix/react@0.11.0",
    "effect@4.1.2",
    "react@^19.2.4"
  ],
  "registryDependencies": [
    "https://gpuixui.sudokw.com/gpuix-icons.json",
    "https://gpuixui.sudokw.com/gpuix-motion.json",
    "https://gpuixui.sudokw.com/gpuix-primitives.json",
    "https://gpuixui.sudokw.com/gpuix-tokens.json",
    "https://gpuixui.sudokw.com/gpuix-tw.json"
  ],
  "files": [
    {
      "path": "packages/components/src/auto-height.tsx",
      "target": "@ui/gpuixui/components/auto-height.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { Props, PublicInstance } from \"@gpuix/react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport {\n  mergeStyles,\n  resolveStyle,\n  type StateStyle,\n} from \"@gpuix/react/floating\";\nimport type { MotionLevel } from \"../motion/timing.js\";\nimport { cn } from \"../tw/index.js\";\nimport * as Option from \"effect/Option\";\nimport {\n  useEffect,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type ReactNode,\n} from \"react\";\nimport { measuredHeight } from \"./internal/measure.js\";\nimport {\n  PresenceShield,\n  delayedTiming,\n  presenceMotion,\n  presenceStyle,\n  useMotionPreference,\n  useSurfacePresence,\n  useSurfaceTiming,\n  type SurfaceTiming,\n} from \"./internal/presence.js\";\nimport { recipeStyle } from \"./internal/recipe.js\";\nimport { useTheme } from \"./internal/theme.js\";\n\nconst FADE_LEAD_MS = 20;\n\nexport interface AutoHeightState {\n  readonly height: number;\n\n  readonly open: boolean;\n\n  readonly reducedMotion: boolean;\n}\n\nexport interface AutoHeightProps extends Omit<Props, \"children\" | \"style\"> {\n  readonly open: boolean;\n  readonly children?: ReactNode;\n\n  readonly level?: MotionLevel;\n\n  readonly timing?: SurfaceTiming;\n\n  readonly className?: string;\n\n  readonly contentClassName?: string;\n\n  readonly style?: StateStyle<AutoHeightState>;\n}\n\nexport const AutoHeight = ({\n  open,\n  level = \"normal\",\n  timing: ownTiming,\n  className,\n  contentClassName,\n  testId,\n  style,\n  children,\n  ...rest\n}: AutoHeightProps) => {\n  const theme = useTheme();\n  const renderer = useGpuix().renderer;\n  const preference = useMotionPreference();\n  const levelTiming = useSurfaceTiming(level);\n  const timing = ownTiming ?? levelTiming;\n  const presence = useSurfacePresence(open, timing);\n  const [measured, setMeasured] = useState(Option.none<number>());\n  const content = useRef<PublicInstance | null>(null);\n\n  const fade = {\n    enter: delayedTiming(timing.enter, FADE_LEAD_MS),\n    exit: timing.exit,\n  };\n\n  useEffect(() => {\n    if (presence.mounted) return;\n    setMeasured((current) =>\n      Option.isSome(current) ? Option.none() : current,\n    );\n  }, [presence.mounted]);\n\n  useLayoutEffect(() => {\n    if (!presence.mounted) return;\n    const instance = content.current;\n    if (instance === null) return;\n    const bounds = renderer?.getElementBounds?.(instance.id) ?? null;\n    if (bounds === null) return;\n    setMeasured((current) =>\n      Option.isSome(current) ? current : Option.some(bounds.height),\n    );\n  }, [presence.mounted, renderer]);\n\n  if (!presence.mounted) return null;\n\n  const height = Option.getOrElse(measured, () => 0);\n  const state: AutoHeightState = {\n    height,\n    open,\n    reducedMotion: preference === \"reduced\",\n  };\n\n  return (\n    <div\n      {...rest}\n      testId={testId}\n      motion={presenceMotion(\n        {\n          initial: { height: 0 },\n          animate: { height: open ? height : 0 },\n          exit: { height: 0 },\n        },\n        presence,\n      )}\n      style={mergeStyles(\n        mergeStyles(\n          {\n            width: \"100%\",\n            overflow: \"hidden\",\n\n            position: \"relative\",\n            ...presenceStyle(presence.present, \"wrapper\"),\n          },\n          recipeStyle(cn(className), theme),\n        ),\n        resolveStyle(style, state),\n      )}\n    >\n      <div\n        ref={content}\n        testId={testId === undefined ? undefined : `${testId}-measured`}\n        motion={presenceMotion(\n          {\n            initial: { opacity: 0 },\n            animate: { opacity: 1 },\n            exit: { opacity: 0 },\n          },\n          presence,\n          fade,\n        )}\n        onLayout={(event) => {\n          Option.match(measuredHeight(event), {\n            onNone: () => undefined,\n            onSome: (next) => setMeasured(Option.some(next)),\n          });\n        }}\n        style={recipeStyle(cn(contentClassName), theme)}\n      >\n        {children}\n      </div>\n      <PresenceShield present={presence.present} />\n    </div>\n  );\n};\n\nexport const autoHeightMeta = {\n  name: \"Auto height\",\n  tier: 5,\n  status: \"done\",\n  gpuixSupport: \"yes\",\n  limitations: [\n    \"the first measurement is read through the renderer's optional `getElementBounds`; a host without it starts the growth on the `onLayout` report a frame later\",\n    \"the clipped box does not reflow its content: a percentage height inside it measures against the content box\",\n    \"the reveal unmounts its subtree after the collapse, so state inside it does not survive a close and the next open measures the body again — a measurement is retired with the subtree it came from, never reused as a stale target\",\n  ],\n  docs: \"docs/auto-height.mdx\",\n  example: \"examples/auto-height.example.tsx\",\n  testFile: \"src/behaviour/auto-height.test.tsx\",\n} as const;\n"
    },
    {
      "path": "packages/components/src/avatar.tsx",
      "target": "@ui/gpuixui/components/avatar.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { Props } from \"@gpuix/react\";\nimport { mergeStyles } from \"@gpuix/react/floating\";\nimport { AVATAR_DISC, AVATAR_FALLBACK, AVATAR_ROOT, cn } from \"../tw/index.js\";\nimport { Match } from \"effect\";\nimport { initials, isAvatarStatus } from \"./avatar/logic.js\";\nimport type { AvatarStatus } from \"./avatar/logic.js\";\nimport { recipeStyle } from \"./internal/recipe.js\";\nimport {\n  avatarPixels,\n  avatarSize,\n  statusTone,\n  useTheme,\n  type Theme,\n} from \"./internal/theme.js\";\n\nexport type AvatarSize = keyof typeof avatarSize;\n\nexport type { AvatarStatus };\n\nexport interface AvatarProps extends Props {\n  readonly name: string;\n\n  readonly src?: string;\n\n  readonly size?: AvatarSize;\n\n  readonly status?: AvatarStatus;\n\n  readonly color?: string;\n\n  readonly initials?: boolean;\n\n  readonly radius?: number;\n\n  readonly clickThrough?: boolean;\n\n  readonly className?: string;\n}\n\nconst DOT = 10;\n\nconst statusFill = (theme: Theme, status: AvatarStatus): string =>\n  Match.value(status).pipe(\n    Match.when(\"online\", () => theme.colors.success),\n    Match.when(\"away\", () => statusTone(theme, \"warning\").foreground),\n    Match.when(\"busy\", () => theme.colors.destructive),\n    Match.when(\"offline\", () => theme.colors.mutedForeground),\n    Match.exhaustive,\n  );\n\nexport const Avatar = ({\n  name,\n  src,\n  size = \"md\",\n  status: statusProp,\n  color,\n  initials: showInitials = true,\n  clickThrough: requestedClickThrough,\n  className,\n  radius,\n  style,\n  ...rest\n}: AvatarProps) => {\n  const clickThrough =\n    requestedClickThrough ??\n    (rest.onClick === undefined &&\n      rest.onMouseDown === undefined &&\n      rest.onMouseUp === undefined);\n  const theme = useTheme();\n  const diameter = avatarPixels(size);\n\n  const status = isAvatarStatus(statusProp) ? statusProp : undefined;\n\n  return (\n    <div\n      role=\"img\"\n      aria-label={name}\n      aria-description={status ?? rest[\"aria-description\"]}\n      {...rest}\n      style={mergeStyles(\n        mergeStyles(recipeStyle(cn(AVATAR_ROOT, className), theme), {\n          width: diameter,\n          height: diameter,\n          ...(clickThrough ? { pointerEvents: \"none\" as const } : {}),\n        }),\n        style,\n      )}\n    >\n      {}\n      <div\n        style={mergeStyles(recipeStyle(AVATAR_DISC, theme), {\n          pointerEvents: \"none\",\n          ...(color === undefined ? {} : { backgroundColor: color }),\n          ...(radius === undefined ? {} : { borderRadius: radius }),\n        })}\n      >\n        {src === undefined && !showInitials ? null : src === undefined ? (\n          <text\n            role=\"presentation\"\n            style={mergeStyles(recipeStyle(AVATAR_FALLBACK, theme), {\n              fontSize: Math.round(diameter * 0.44),\n            })}\n          >\n            {initials(name)}\n          </text>\n        ) : (\n          <img\n            src={src}\n            alt=\"\"\n            role=\"presentation\"\n            objectFit=\"cover\"\n            style={{ width: \"100%\", height: \"100%\" }}\n          />\n        )}\n      </div>\n      {status === undefined ? null : (\n        <div\n          style={{\n            position: \"absolute\",\n            right: -1,\n            bottom: -1,\n            width: DOT,\n            height: DOT,\n            borderRadius: theme.radius.full,\n            backgroundColor: statusFill(theme, status),\n            borderWidth: theme.borderWidth.strong,\n            borderColor: theme.colors.card,\n            pointerEvents: \"none\",\n          }}\n        />\n      )}\n    </div>\n  );\n};\n\nexport const avatarMeta = {\n  name: \"Avatars\",\n  tier: 2,\n  status: \"done\",\n  gpuixSupport: \"yes\",\n  limitations: [\n    \"no image loading or error state in v1: the initials fallback is what shows until the picture paints\",\n  ],\n  docs: \"docs/avatar.mdx\",\n  example: \"examples/avatar.example.tsx\",\n  testFile: \"src/behaviour/avatar.test.tsx\",\n} as const;\n"
    },
    {
      "path": "packages/components/src/avatar/logic.ts",
      "target": "@ui/gpuixui/components/avatar/logic.ts",
      "type": "registry:lib",
      "content": "import { pipe } from \"effect\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Schema from \"effect/Schema\";\nimport * as Str from \"effect/String\";\n\nexport const AVATAR_STATUSES = [\"online\", \"away\", \"busy\", \"offline\"] as const;\n\nexport const AvatarStatus = Schema.Literals(AVATAR_STATUSES);\n\nexport type AvatarStatus = typeof AvatarStatus.Type;\n\nexport const isAvatarStatus = Schema.is(AvatarStatus);\n\nconst SEPARATORS = /[\\s-]+/;\n\nconst parts = (name: string): ReadonlyArray<string> =>\n  pipe(Str.trim(name), Str.split(SEPARATORS), Arr.filter(Str.isNonEmpty));\n\nconst leadingLetters = (name: string): ReadonlyArray<string> =>\n  Arr.getSomes(\n    Arr.map(parts(name), (part) =>\n      Option.map(Arr.head(Arr.fromIterable(part)), Str.toUpperCase),\n    ),\n  );\n\nexport const initials = (name: string): string => {\n  const letters = leadingLetters(name);\n\n  const last = Option.getOrElse(Arr.last(letters), () => \"\");\n\n  return Option.match(Arr.head(letters), {\n    onNone: () => \"?\",\n    onSome: (first) => (first === last ? first : `${first}${last}`),\n  });\n};\n"
    },
    {
      "path": "packages/components/src/collapsible.tsx",
      "target": "@ui/gpuixui/components/collapsible.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { Props } from \"@gpuix/react\";\nimport {\n  mergeStyles,\n  resolveStyle,\n  type StateStyle,\n} from \"@gpuix/react/floating\";\nimport {\n  cn,\n  COLLAPSIBLE_CONTENT,\n  COLLAPSIBLE_ROOT,\n  COLLAPSIBLE_TRIGGER,\n} from \"../tw/index.js\";\nimport { createContext, useContext, useState } from \"react\";\nimport { AutoHeight } from \"./auto-height.js\";\nimport { useControllableState } from \"./internal/controllable.js\";\nimport { activationHandlers } from \"./internal/key.js\";\nimport {\n  assertParts,\n  type PartChildren,\n  type PartComponent,\n  type PartsContract,\n} from \"./internal/parts.js\";\nimport { recipeStyle } from \"./internal/recipe.js\";\nimport {\n  controlRing,\n  disabledFade,\n  pressWash,\n  useTheme,\n} from \"./internal/theme.js\";\n\ninterface CollapsibleContextValue {\n  readonly open: boolean;\n  readonly setOpen: (open: boolean) => void;\n}\n\nconst CollapsibleContext = createContext<CollapsibleContextValue | undefined>(\n  undefined,\n);\n\nconst useCollapsibleContext = (): CollapsibleContextValue => {\n  const value = useContext(CollapsibleContext);\n  if (value === undefined) {\n    throw new Error(\"Collapsible parts must be used inside Collapsible\");\n  }\n  return value;\n};\n\nexport interface CollapsibleProps {\n  readonly open?: boolean;\n\n  readonly defaultOpen?: boolean;\n  readonly onOpenChange?: (open: boolean) => void;\n  readonly children?: PartChildren;\n}\n\nexport const Collapsible = ({\n  open: openProp,\n  defaultOpen = false,\n  onOpenChange,\n  children,\n}: CollapsibleProps) => {\n  const theme = useTheme();\n  const [open, setOpen] = useControllableState({\n    value: openProp,\n    defaultValue: defaultOpen,\n    onChange: onOpenChange,\n  });\n  assertParts(collapsibleRoot, children);\n\n  return (\n    <CollapsibleContext.Provider value={{ open, setOpen }}>\n      <div style={recipeStyle(COLLAPSIBLE_ROOT, theme)}>{children}</div>\n    </CollapsibleContext.Provider>\n  );\n};\n\nexport interface CollapsibleTriggerProps extends Omit<Props, \"style\"> {\n  readonly className?: string;\n\n  readonly style?: StateStyle<CollapsibleTriggerState>;\n\n  readonly disabled?: boolean;\n}\n\nexport interface CollapsibleTriggerState {\n  readonly open: boolean;\n  readonly closed: boolean;\n  readonly focused: boolean;\n  readonly disabled: boolean;\n}\n\nexport const CollapsibleTrigger = ({\n  onClick,\n  onKeyDown,\n  onKeyUp,\n  onFocus,\n  onBlur,\n  className,\n  style,\n  disabled = false,\n  ...rest\n}: CollapsibleTriggerProps) => {\n  const theme = useTheme();\n  const { open, setOpen } = useCollapsibleContext();\n  const [focused, setFocused] = useState(false);\n  const state: CollapsibleTriggerState = {\n    open,\n    closed: !open,\n    focused,\n    disabled,\n  };\n\n  const toggle = (): void => {\n    if (!disabled) setOpen(!open);\n  };\n\n  return (\n    <div\n      role=\"button\"\n      aria-expanded={open}\n      {...rest}\n      tabIndex={disabled ? -1 : 0}\n      onClick={(event) => {\n        onClick?.(event);\n        toggle();\n      }}\n      {...activationHandlers(toggle, { onKeyDown, onKeyUp })}\n      onFocus={(event) => {\n        setFocused(true);\n        onFocus?.(event);\n      }}\n      onBlur={(event) => {\n        setFocused(false);\n        onBlur?.(event);\n      }}\n      style={mergeStyles(\n        mergeStyles(recipeStyle(cn(COLLAPSIBLE_TRIGGER, className), theme), {\n          cursor: disabled ? \"not-allowed\" : \"pointer\",\n          ...disabledFade(disabled),\n          hover: disabled ? undefined : { backgroundColor: theme.colors.hover },\n          active: disabled\n            ? undefined\n            : { backgroundColor: pressWash(theme, \"hover\") },\n          ...controlRing(theme, { disabled }),\n        }),\n        resolveStyle(style, state),\n      )}\n    />\n  );\n};\n\nexport interface CollapsibleContentProps extends Props {\n  readonly className?: string;\n}\n\nexport const CollapsibleContent = ({\n  className,\n  style,\n  children,\n  ...rest\n}: CollapsibleContentProps) => {\n  const { open } = useCollapsibleContext();\n\n  return (\n    <AutoHeight\n      {...rest}\n      open={open}\n      contentClassName={cn(COLLAPSIBLE_CONTENT, className)}\n      style={style}\n    >\n      {children}\n    </AutoHeight>\n  );\n};\n\nconst collapsibleParts: ReadonlyArray<PartComponent> = [\n  Collapsible,\n  CollapsibleTrigger,\n  CollapsibleContent,\n];\n\nconst collapsibleRoot: PartsContract = {\n  parent: \"Collapsible\",\n  family: collapsibleParts,\n  accepts: [CollapsibleTrigger, CollapsibleContent],\n  hint: \"Put CollapsibleTrigger and CollapsibleContent directly inside Collapsible.\",\n};\n\nexport const collapsibleMeta = {\n  name: \"Collapsible sections\",\n  tier: 2,\n  status: \"done\",\n  gpuixSupport: \"yes\",\n  limitations: [\n    \"the body unmounts after its collapse tween, so state inside it does not survive a close\",\n  ],\n  docs: \"docs/collapsible.mdx\",\n  example: \"examples/collapsible.example.tsx\",\n  testFile: \"src/behaviour/collapsible.test.tsx\",\n} as const;\n"
    },
    {
      "path": "packages/components/src/internal/button.tsx",
      "target": "@ui/gpuixui/components/internal/button.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { Props } from \"@gpuix/react\";\nimport type { ReactNode } from \"react\";\nimport {\n  Button as PrimitiveButton,\n  IconButton as PrimitiveIconButton,\n  actionOnColor as primitiveActionOnColor,\n  type ButtonVariant as PrimitiveVariant,\n} from \"../../primitives/button.js\";\nimport { cn } from \"../../tw/index.js\";\nimport * as Schema from \"effect/Schema\";\nimport type { Theme } from \"./theme.js\";\n\nconst ButtonVariants = Schema.Literals([\n  \"primary\",\n  \"secondary\",\n  \"ghost\",\n  \"danger\",\n  \"link\",\n]);\n\nexport type ButtonVariant = typeof ButtonVariants.Type;\n\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps extends Props {\n  readonly variant?: ButtonVariant;\n\n  readonly size?: ButtonSize;\n\n  readonly disabled?: boolean;\n\n  readonly loading?: boolean;\n\n  readonly loadingLabel?: string;\n\n  readonly reducedMotion?: boolean;\n\n  readonly leading?: ReactNode;\n\n  readonly trailing?: ReactNode;\n}\n\nexport interface IconButtonProps extends Props {\n  readonly label: string;\n\n  readonly size?: ButtonSize;\n  readonly disabled?: boolean;\n}\n\ninterface Look {\n  readonly variant: PrimitiveVariant;\n  readonly className: string;\n}\n\nconst LOOKS: Readonly<Record<ButtonVariant, Look>> = {\n  primary: { variant: \"primary\", className: \"\" },\n  secondary: { variant: \"secondary\", className: \"border border-strong\" },\n  ghost: { variant: \"ghost\", className: \"\" },\n  danger: { variant: \"destructive\", className: \"\" },\n\n  link: { variant: \"link\", className: \"underline underline-offset-4\" },\n};\n\nconst lookOf = (variant: unknown): Look =>\n  LOOKS[Schema.is(ButtonVariants)(variant) ? variant : \"secondary\"];\n\nexport const actionOnColor = (theme: Theme, variant: ButtonVariant): string =>\n  primitiveActionOnColor(theme, lookOf(variant).variant);\n\nexport const Button = ({\n  variant = \"secondary\",\n  size = \"md\",\n  disabled = false,\n  onClick,\n  style,\n  \"aria-label\": ariaLabel,\n  ...rest\n}: ButtonProps) => {\n  const look = lookOf(variant);\n  return (\n    <PrimitiveButton\n      {...rest}\n      variant={look.variant}\n      size={size}\n      disabled={disabled}\n      label={ariaLabel}\n      className={cn(look.className)}\n      onActivate={onClick}\n      style={style}\n    />\n  );\n};\n\nexport const IconButton = ({\n  label,\n  size = \"md\",\n  disabled = false,\n  onClick,\n  style,\n  children,\n  ...rest\n}: IconButtonProps) => (\n  <PrimitiveIconButton\n    {...rest}\n    icon={children}\n    variant=\"ghost\"\n    size={size}\n    disabled={disabled}\n    label={label}\n    className=\"text-muted-foreground hover:text-foreground\"\n    onActivate={onClick}\n    style={style}\n  />\n);\n"
    },
    {
      "path": "packages/components/src/internal/controllable.ts",
      "target": "@ui/gpuixui/components/internal/controllable.ts",
      "type": "registry:lib",
      "content": "import { useCallback, useState } from \"react\";\n\nexport type ControllableState<Value> = readonly [Value, (next: Value) => void];\n\nexport interface ControllableStateOptions<Value> {\n  readonly value: Value | undefined;\n\n  readonly defaultValue: Value;\n\n  readonly onChange?: (next: Value) => void;\n}\n\nexport const useControllableState = <Value>({\n  value,\n  defaultValue,\n  onChange,\n}: ControllableStateOptions<Value>): ControllableState<Value> => {\n  const [internal, setInternal] = useState(defaultValue);\n  const controlled = value !== undefined;\n\n  const setValue = useCallback(\n    (next: Value) => {\n      if (!controlled) setInternal(next);\n      if (value !== next) onChange?.(next);\n    },\n    [controlled, value, onChange],\n  );\n\n  return [controlled ? value : internal, setValue];\n};\n"
    },
    {
      "path": "packages/components/src/internal/key.ts",
      "target": "@ui/gpuixui/components/internal/key.ts",
      "type": "registry:lib",
      "content": "import type { EventPayload, KeyEvent, Props } from \"@gpuix/react\";\nimport * as Option from \"effect/Option\";\n\nexport type KeyPhase = \"down\" | \"up\";\n\nexport const isActivation = (event: EventPayload, phase: KeyPhase): boolean => {\n  if (event.isHeld === true) return false;\n  if (event.key === \"enter\") return phase === \"down\";\n  return event.key === \"space\" && phase === \"up\";\n};\n\nexport const swallowsDefault = (\n  event: EventPayload,\n  phase: KeyPhase,\n): boolean => event.key === \"space\" && phase === \"down\";\n\nexport const activationHandlers = (\n  activate: (event: KeyEvent) => void,\n  observe: Pick<Props, \"onKeyDown\" | \"onKeyUp\"> = {},\n): Pick<Props, \"onKeyDown\" | \"onKeyUp\"> => ({\n  onKeyDown: (event: KeyEvent) => {\n    if (swallowsDefault(event, \"down\")) event.preventDefault();\n    if (isActivation(event, \"down\")) activate(event);\n    observe.onKeyDown?.(event);\n  },\n  onKeyUp: (event: KeyEvent) => {\n    if (isActivation(event, \"up\")) activate(event);\n    observe.onKeyUp?.(event);\n  },\n});\n\nexport const isEnter = (event: EventPayload): boolean => event.key === \"enter\";\n\nexport const isDown = (event: EventPayload): boolean =>\n  event.key === \"down\" || (event.key === \"n\" && event.modifiers?.ctrl === true);\n\nexport const isUp = (event: EventPayload): boolean =>\n  event.key === \"up\" || (event.key === \"p\" && event.modifiers?.ctrl === true);\n\nexport const isRight = (event: EventPayload): boolean => event.key === \"right\";\n\nexport const isLeft = (event: EventPayload): boolean => event.key === \"left\";\n\nexport const isFirst = (event: EventPayload): boolean => event.key === \"home\";\n\nexport const isLast = (event: EventPayload): boolean => event.key === \"end\";\n\nexport const isPageUp = (event: EventPayload): boolean =>\n  event.key === \"pageup\";\n\nexport const isPageDown = (event: EventPayload): boolean =>\n  event.key === \"pagedown\";\n\nexport const isDismiss = (event: EventPayload): boolean =>\n  event.key === \"escape\";\n\nexport const isTab = (event: EventPayload): boolean => event.key === \"tab\";\n\nexport const isSelectAll = (event: EventPayload): boolean =>\n  event.key === \"a\" &&\n  (event.modifiers?.cmd === true || event.modifiers?.ctrl === true);\n\nexport const isMenuKey = (event: EventPayload): boolean =>\n  (event.key === \"f10\" && event.modifiers?.shift === true) ||\n  event.key === \"contextmenu\";\n\nexport const axisDirection = (\n  event: EventPayload,\n  orientation: \"horizontal\" | \"vertical\",\n): Option.Option<1 | -1> => {\n  if (event.key === \"right\") return Option.some(1);\n  if (event.key === \"left\") return Option.some(-1);\n  if (orientation === \"vertical\") {\n    if (isDown(event)) return Option.some(1);\n    if (isUp(event)) return Option.some(-1);\n  }\n  return Option.none();\n};\n"
    },
    {
      "path": "packages/components/src/internal/measure.ts",
      "target": "@ui/gpuixui/components/internal/measure.ts",
      "type": "registry:lib",
      "content": "import type { EventPayload } from \"@gpuix/react\";\nimport * as Option from \"effect/Option\";\n\nexport interface MeasuredBox {\n  readonly x: number;\n  readonly y: number;\n  readonly width: number;\n  readonly height: number;\n}\n\nconst measurable = (value: number | undefined): Option.Option<number> =>\n  Option.filter(Option.fromNullishOr(value), Number.isFinite);\n\nexport const measuredBox = (event: EventPayload): Option.Option<MeasuredBox> =>\n  Option.all({\n    x: measurable(event.x),\n    y: measurable(event.y),\n    width: measurable(event.width),\n    height: measurable(event.height),\n  });\n\nexport const measuredHeight = (event: EventPayload): Option.Option<number> =>\n  measurable(event.height);\n\nexport interface MeasuredPoint {\n  readonly x: number;\n  readonly y: number;\n}\n\nexport const measuredPoint = (\n  event: EventPayload,\n): Option.Option<MeasuredPoint> =>\n  Option.all({\n    x: measurable(event.x),\n    y: measurable(event.y),\n  });\n"
    },
    {
      "path": "packages/components/src/internal/parts.ts",
      "target": "@ui/gpuixui/components/internal/parts.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as P from \"effect/Predicate\";\nimport {\n  Children,\n  Fragment,\n  isValidElement,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\n\nexport type PartComponent = (props: never) => ReactNode;\n\nexport type PartChildren =\n  | ReactElement<unknown>\n  | boolean\n  | null\n  | undefined\n  | ReadonlyArray<PartChildren>;\n\nexport interface PartsContract {\n  readonly parent: string;\n\n  readonly family: ReadonlyArray<PartComponent>;\n\n  readonly accepts: ReadonlyArray<PartComponent>;\n\n  readonly hint?: string;\n}\n\nconst componentName = (type: unknown): string =>\n  P.isString(type)\n    ? type\n    : P.isFunction(type)\n      ? type.name || \"anonymous\"\n      : \"element\";\n\nconst fragmentChildren = (element: ReactElement<unknown>): ReactNode =>\n  hasChildren(element.props) ? element.props.children : undefined;\n\nconst hasChildren = (\n  value: unknown,\n): value is { readonly children?: ReactNode } =>\n  P.isObject(value) && \"children\" in value;\n\nconst leaves = (children: ReactNode): ReadonlyArray<unknown> =>\n  Arr.flatMap(Children.toArray(children), (child) =>\n    isValidElement<unknown>(child) && child.type === Fragment\n      ? leaves(fragmentChildren(child))\n      : [child],\n  );\n\nexport const assertParts = (\n  contract: PartsContract,\n  children: ReactNode,\n): void => {\n  Arr.forEach(leaves(children), (leaf) => {\n    if (!isValidElement(leaf)) return;\n    if (Arr.some(contract.accepts, (part) => part === leaf.type)) return;\n    if (!Arr.some(contract.family, (part) => part === leaf.type)) return;\n\n    throw new Error(\n      `${componentName(leaf.type)} cannot be used inside ${contract.parent}.` +\n        (contract.hint === undefined ? \"\" : ` ${contract.hint}`),\n    );\n  });\n};\n"
    },
    {
      "path": "packages/components/src/internal/presence.tsx",
      "target": "@ui/gpuixui/components/internal/presence.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { MotionProps } from \"@gpuix/react\";\nimport {\n  delayedTiming,\n  exitWindowMs,\n  fadeFrom,\n  fadeFromAnchor,\n  fadeFromSide,\n  fadeOnly,\n  presenceMotion as motionOfPresence,\n  presenceStyle,\n  transitionWindowMs,\n  type PresenceFrames,\n  type PresenceHitRule,\n  type PresenceSide,\n  type PresenceTiming,\n  type PresenceTransition,\n} from \"../../motion/presence.js\";\nimport {\n  MotionProvider,\n  useMotionPreference,\n  useSurfacePresence,\n  useSurfaceTiming,\n  type MotionProviderProps,\n  type SurfacePresence,\n} from \"../../motion/react/presence.js\";\nimport {\n  motionTiming,\n  type MotionLevel,\n  type MotionPreference,\n} from \"../../motion/timing.js\";\n\nexport { MotionProvider, useMotionPreference, type MotionProviderProps };\n\nexport type SurfaceFrames = PresenceFrames;\n\nexport type SurfaceTiming = PresenceTiming;\n\nexport type PanelSide = PresenceSide;\n\nexport {\n  delayedTiming,\n  exitWindowMs,\n  fadeFrom,\n  fadeFromAnchor,\n  fadeFromSide,\n  fadeOnly,\n  presenceStyle,\n  transitionWindowMs,\n};\nexport type { PresenceHitRule };\n\nexport { useSurfacePresence, useSurfaceTiming, type SurfacePresence };\n\nexport const moveTiming = (\n  level: MotionLevel,\n  preference: MotionPreference,\n): PresenceTransition => motionTiming(level, { phase: \"standard\", preference });\n\nexport const useMoveTiming = (level: MotionLevel): PresenceTransition =>\n  moveTiming(level, useMotionPreference());\n\nexport const presenceMotion = (\n  frames: SurfaceFrames,\n  presence: SurfacePresence,\n  timing: SurfaceTiming = presence.timing,\n): MotionProps => motionOfPresence(frames, presence.present, timing);\n\nexport const PresenceShield = ({ present }: { readonly present: boolean }) =>\n  present ? null : (\n    <div\n      testId=\"presence-shield\"\n      style={{ position: \"absolute\", top: 0, right: 0, bottom: 0, left: 0 }}\n    />\n  );\n\nexport const presenceTabStop = (\n  present: boolean,\n): { readonly tabIndex?: number } => (present ? {} : { tabIndex: -1 });\n"
    },
    {
      "path": "packages/components/src/internal/recipe.ts",
      "target": "@ui/gpuixui/components/internal/recipe.ts",
      "type": "registry:lib",
      "content": "import type { StyleDesc } from \"@gpuix/react\";\nimport { createRecipeCache } from \"../../tw/index.js\";\nimport type { Theme } from \"./theme.js\";\n\nexport const recipeStyle: (recipe: string, theme: Theme) => StyleDesc =\n  createRecipeCache(512);\n"
    },
    {
      "path": "packages/components/src/internal/text.ts",
      "target": "@ui/gpuixui/components/internal/text.ts",
      "type": "registry:lib",
      "content": "import { pipe } from \"effect\";\nimport * as Option from \"effect/Option\";\nimport * as Str from \"effect/String\";\n\nexport const textOf = (value: unknown): string =>\n  typeof value === \"string\" ? value : \"\";\n\nexport const nameOf = (value: unknown): string | undefined =>\n  pipe(\n    Option.some(textOf(value)),\n    Option.map(Str.trim),\n    Option.filter(Str.isNonEmpty),\n    Option.getOrUndefined,\n  );\n"
    },
    {
      "path": "packages/components/src/internal/theme.ts",
      "target": "@ui/gpuixui/components/internal/theme.ts",
      "type": "registry:lib",
      "content": "import type { StyleDesc } from \"@gpuix/react\";\nimport * as Schema from \"effect/Schema\";\nimport {\n  alpha,\n  avatarPixels,\n  avatarSize,\n  brandTextOn,\n  controlFocusRing,\n  controlHeight,\n  controlPaddingX,\n  currentPlatform,\n  darkTheme,\n  disabledAction,\n  disabledOpacity,\n  elevationShadow,\n  filledHover,\n  filledPressed,\n  findHighlight,\n  focusRing,\n  keyboardFocusRing,\n  lightTheme,\n  nativeTextTheme,\n  pointerWash,\n  selectionOn,\n  statusTone,\n  syntaxColors,\n  targetSize,\n  textStyle,\n  type BoxShadow,\n  type ElevationLevel,\n  type FindHighlightSource,\n  type NativeTextFill,\n  type PlatformName,\n  type PointerWashSurface,\n  type StatusTone,\n  type SyntaxColors,\n  type SyntaxRole,\n  type TextRole,\n  type Theme,\n  type ThemeColors,\n} from \"../../tokens/index.js\";\n\nexport {\n  alpha,\n  avatarPixels,\n  avatarSize,\n  controlFocusRing,\n  controlHeight,\n  controlPaddingX,\n  currentPlatform,\n  darkTheme,\n  disabledAction,\n  disabledOpacity,\n  elevationShadow,\n  filledHover,\n  filledPressed,\n  findHighlight,\n  focusRing,\n  keyboardFocusRing,\n  lightTheme,\n  nativeTextTheme,\n  statusTone,\n  syntaxColors,\n  targetSize,\n  textStyle,\n};\nexport type {\n  BoxShadow,\n  ElevationLevel,\n  FindHighlightSource,\n  NativeTextFill,\n  PlatformName,\n  StatusTone,\n  SyntaxColors,\n  SyntaxRole,\n  TextRole,\n  Theme,\n};\nexport { ThemeProvider, useTheme } from \"../../tokens/react.js\";\n\nexport const keyboardRing = (theme: Theme, invalid = false): StyleDesc =>\n  controlFocusRing(theme, { invalid }) ?? {};\n\nexport interface ControlRingState {\n  readonly disabled?: boolean;\n\n  readonly invalid?: boolean;\n}\n\nexport const controlRing = (\n  theme: Theme,\n  state: ControlRingState = {},\n): StyleDesc => controlFocusRing(theme, state) ?? {};\n\nexport const pressWash = (theme: Theme, replaced: PointerWashSurface): string =>\n  pointerWash(theme, \"active\", replaced) ?? theme.colors.active;\n\nexport const selectionWash = (theme: Theme): StyleDesc => ({\n  selectionColor: theme.colors.selection,\n});\n\nexport const keepContent: StyleDesc = { keyboardFocusDim: false };\n\nexport const disabledFade = (\n  disabled: boolean,\n): { readonly opacity: number } | undefined =>\n  disabled ? { opacity: disabledOpacity } : undefined;\n\nconst Tones = Schema.Literals([\"info\", \"success\", \"warning\", \"danger\"]);\n\nexport type Tone = typeof Tones.Type;\n\nexport const toneOf = (value: unknown): Tone =>\n  Schema.is(Tones)(value) ? value : \"info\";\n\nexport interface ToneStyle {\n  readonly fg: string;\n  readonly bg: string;\n  readonly border: string;\n}\n\nconst TOKEN_TONE: Readonly<Record<Tone, StatusTone>> = {\n  info: \"info\",\n  success: \"success\",\n  warning: \"warning\",\n  danger: \"destructive\",\n};\n\nexport const tone = (theme: Theme, value: Tone): ToneStyle => {\n  const { background, border, foreground } = statusTone(\n    theme,\n    TOKEN_TONE[value],\n  );\n  return { bg: background, border, fg: foreground };\n};\n\nconst softRoles: Readonly<Record<Tone, keyof ThemeColors>> = {\n  info: \"infoSoft\",\n  success: \"successSoft\",\n  warning: \"warningSoft\",\n  danger: \"destructiveSoft\",\n};\n\nexport const messageLink = (theme: Theme, value: Tone): string =>\n  brandTextOn(theme, softRoles[value]);\n\nexport const messageSelection = (theme: Theme, value: Tone): string =>\n  selectionOn(theme, softRoles[value]);\n\nexport const toneSurface = (theme: Theme, value: Tone): StyleDesc => {\n  const { bg, border, fg } = tone(theme, value);\n  return {\n    backgroundColor: bg,\n    color: fg,\n    borderWidth: theme.borderWidth.thin,\n    borderColor: border,\n  };\n};\n"
    },
    {
      "path": "packages/components/src/message.tsx",
      "target": "@ui/gpuixui/components/message.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { Props } from \"@gpuix/react\";\nimport {\n  mergeStyles,\n  resolveStyle,\n  type StateStyle,\n} from \"@gpuix/react/floating\";\nimport { Icon } from \"../icons/index.js\";\nimport { accessibleName, visibleCopy } from \"../primitives/core/index.js\";\nimport {\n  cn,\n  MESSAGE_ACTIONS,\n  MESSAGE_CONTENT,\n  MESSAGE_FOOTER,\n  MESSAGE_GROUP,\n  MESSAGE_HEADER,\n  MESSAGE_REASONING,\n  MESSAGE_REASONING_CONTENT,\n  MESSAGE_REASONING_TRIGGER,\n  MESSAGE_ROOT,\n  MESSAGE_SOURCE,\n  MESSAGE_SOURCES,\n  MESSAGE_STREAMING,\n  MESSAGE_TOOL_CALL,\n  MESSAGE_TOOL_CALL_HEAD,\n  MESSAGE_TOOL_CALL_NAME,\n  MESSAGE_TOOL_CALL_RESULT,\n  MESSAGE_TOOL_CALL_STATUS,\n} from \"../tw/index.js\";\nimport * as Schema from \"effect/Schema\";\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport { Avatar } from \"./avatar.js\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"./collapsible.js\";\nimport { Button } from \"./internal/button.js\";\nimport {\n  assertParts,\n  type PartChildren,\n  type PartComponent,\n  type PartsContract,\n} from \"./internal/parts.js\";\nimport { recipeStyle } from \"./internal/recipe.js\";\nimport { textOf } from \"./internal/text.js\";\nimport { useTheme } from \"./internal/theme.js\";\n\nconst MessageVariants = Schema.Literals([\"user\", \"assistant\", \"system\"]);\n\nexport type MessageVariant = typeof MessageVariants.Type;\n\nconst AUTHOR: Readonly<Record<MessageVariant, string>> = {\n  user: \"You\",\n  assistant: \"Assistant\",\n  system: \"System\",\n};\n\nconst rowVariant = (variant: unknown): MessageVariant =>\n  Schema.is(MessageVariants)(variant) ? variant : \"assistant\";\n\nconst MessageAlignments = Schema.Literals([\"start\", \"end\"]);\n\nexport type MessageAlign = typeof MessageAlignments.Type;\n\nconst rowAlign = (align: unknown, variant: MessageVariant): MessageAlign =>\n  Schema.is(MessageAlignments)(align)\n    ? align\n    : variant === \"user\"\n      ? \"end\"\n      : \"start\";\n\nconst listOf = <A,>(value: ReadonlyArray<A> | undefined): ReadonlyArray<A> =>\n  Array.isArray(value) ? value : [];\n\nconst MessageToolStatuses = Schema.Literals([\"running\", \"complete\", \"error\"]);\n\nexport type MessageToolStatus = typeof MessageToolStatuses.Type;\n\nexport interface MessageToolCall {\n  readonly name: string;\n  readonly status: MessageToolStatus;\n\n  readonly result?: string;\n}\n\nexport interface MessageSource {\n  readonly label: string;\n\n  readonly href?: string;\n}\n\ninterface MessageContextValue {\n  readonly variant: MessageVariant;\n  readonly align: MessageAlign;\n\n  readonly author: string;\n\n  readonly timestamp: string | undefined;\n\n  readonly meta: ReactNode;\n\n  readonly avatarSrc: string | undefined;\n}\n\nconst MessageContext = createContext<MessageContextValue | undefined>(\n  undefined,\n);\n\nconst useMessage = (): MessageContextValue => {\n  const value = useContext(MessageContext);\n  if (value === undefined) {\n    throw new Error(\"Message parts must be used inside Message\");\n  }\n  return value;\n};\n\nconst TOOL_STATUS: Readonly<Record<MessageToolStatus, string>> = {\n  running: \"Running\",\n  complete: \"Done\",\n  error: \"Failed\",\n};\n\nconst TOOL_STATUS_UNKNOWN = \"Unknown\";\n\nconst toolStatusWord = (status: unknown): string =>\n  Schema.is(MessageToolStatuses)(status)\n    ? TOOL_STATUS[status]\n    : TOOL_STATUS_UNKNOWN;\n\nexport interface MessageProps extends Omit<Props, \"children\"> {\n  readonly variant?: MessageVariant;\n\n  readonly align?: MessageAlign;\n\n  readonly author?: string;\n\n  readonly timestamp?: string;\n\n  readonly meta?: ReactNode;\n\n  readonly avatarSrc?: string;\n\n  readonly label?: string;\n\n  readonly className?: string;\n  readonly children?: PartChildren;\n}\n\nexport const Message = ({\n  variant: variantProp = \"assistant\",\n  align: alignProp,\n  author,\n  timestamp,\n  meta = null,\n  avatarSrc,\n  label,\n  className,\n  children,\n  style,\n  ...rest\n}: MessageProps) => {\n  const theme = useTheme();\n  assertParts(messageRoot, children);\n  const variant = rowVariant(variantProp);\n  const align = rowAlign(alignProp, variant);\n  const name = accessibleName(textOf(author)) ?? AUTHOR[variant];\n  const time = accessibleName(textOf(timestamp));\n\n  return (\n    <MessageContext.Provider\n      value={{\n        variant,\n        align,\n        author: name,\n        timestamp: time,\n        meta,\n        avatarSrc,\n      }}\n    >\n      <div\n        role=\"group\"\n        aria-label={accessibleName(textOf(label)) ?? name}\n        {...rest}\n        style={mergeStyles(\n          mergeStyles(recipeStyle(cn(MESSAGE_ROOT, className), theme), {\n            flexDirection: align === \"end\" ? \"row-reverse\" : \"row\",\n            alignItems: variant === \"system\" ? \"center\" : \"flex-start\",\n          }),\n          style,\n        )}\n      >\n        {children}\n      </div>\n    </MessageContext.Provider>\n  );\n};\n\nexport interface MessageAvatarProps extends Omit<Props, \"children\"> {\n  readonly name?: string;\n\n  readonly src?: string;\n\n  readonly className?: string;\n}\n\nexport const MessageAvatar = ({\n  name,\n  src,\n  className,\n  ...rest\n}: MessageAvatarProps) => {\n  const { author, avatarSrc } = useMessage();\n  return (\n    <Avatar\n      {...rest}\n      name={name ?? author}\n      src={src ?? avatarSrc}\n      size=\"sm\"\n      className={className}\n    />\n  );\n};\n\nexport interface MessageHeaderProps extends Omit<Props, \"children\"> {\n  readonly className?: string;\n}\n\nexport const MessageHeader = ({\n  className,\n  style,\n  ...rest\n}: MessageHeaderProps) => {\n  const theme = useTheme();\n  const { author, timestamp, meta } = useMessage();\n  return (\n    <div\n      {...rest}\n      style={mergeStyles(\n        mergeStyles(recipeStyle(cn(MESSAGE_HEADER, className), theme), {\n          alignSelf: \"flex-start\",\n        }),\n        style,\n      )}\n    >\n      <text>{author}</text>\n      {timestamp === undefined ? null : <text>{timestamp}</text>}\n      {meta}\n    </div>\n  );\n};\n\nexport interface MessageFooterProps extends Omit<Props, \"children\"> {\n  readonly className?: string;\n  readonly children?: PartChildren;\n}\n\nexport const MessageFooter = ({\n  className,\n  children,\n  style,\n  ...rest\n}: MessageFooterProps) => {\n  const theme = useTheme();\n  const { align } = useMessage();\n  assertParts(messageFooter, children);\n  return (\n    <div\n      {...rest}\n      style={mergeStyles(\n        mergeStyles(recipeStyle(cn(MESSAGE_FOOTER, className), theme), {\n          alignSelf: align === \"end\" ? \"flex-end\" : \"flex-start\",\n          justifyContent: align === \"end\" ? \"flex-end\" : \"flex-start\",\n        }),\n        style,\n      )}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport interface MessageContentState {\n  readonly variant: MessageVariant;\n  readonly align: MessageAlign;\n  readonly streaming: boolean;\n}\n\nexport interface MessageContentProps extends Omit<Props, \"children\" | \"style\"> {\n  readonly children?: ReactNode;\n\n  readonly reasoning?: string;\n\n  readonly toolCalls?: ReadonlyArray<MessageToolCall>;\n\n  readonly sources?: ReadonlyArray<MessageSource>;\n\n  readonly streaming?: boolean;\n\n  readonly streamingLabel?: string;\n\n  readonly reasoningLabel?: string;\n\n  readonly sourcesLabel?: string;\n\n  readonly className?: string;\n\n  readonly style?: StateStyle<MessageContentState>;\n}\n\nconst Reasoning = ({\n  text,\n  label,\n}: {\n  readonly text: string;\n  readonly label: string;\n}) => {\n  const theme = useTheme();\n\n  return (\n    <div style={recipeStyle(MESSAGE_REASONING, theme)}>\n      <Collapsible>\n        <CollapsibleTrigger\n          testId=\"message-reasoning-trigger\"\n          style={recipeStyle(MESSAGE_REASONING_TRIGGER, theme)}\n        >\n          <text>{label}</text>\n          <Icon\n            name=\"chevron-down\"\n            size=\"xs\"\n            color={theme.colors.mutedForeground}\n          />\n        </CollapsibleTrigger>\n        <CollapsibleContent\n          testId=\"message-reasoning-content\"\n          style={recipeStyle(MESSAGE_REASONING_CONTENT, theme)}\n        >\n          <text>{text}</text>\n        </CollapsibleContent>\n      </Collapsible>\n    </div>\n  );\n};\n\nconst ToolCall = ({ call }: { readonly call: MessageToolCall }) => {\n  const theme = useTheme();\n\n  return (\n    <div style={recipeStyle(MESSAGE_TOOL_CALL, theme)}>\n      <div style={recipeStyle(MESSAGE_TOOL_CALL_HEAD, theme)}>\n        <text style={recipeStyle(MESSAGE_TOOL_CALL_NAME, theme)}>\n          {call.name}\n        </text>\n        <div style={recipeStyle(MESSAGE_TOOL_CALL_STATUS, theme)}>\n          {call.status === \"error\" ? (\n            <Icon name=\"error\" size=\"xs\" color={theme.colors.destructive} />\n          ) : null}\n          <text>{toolStatusWord(call.status)}</text>\n        </div>\n      </div>\n      {call.result === undefined ? null : (\n        <text style={recipeStyle(MESSAGE_TOOL_CALL_RESULT, theme)}>\n          {call.result}\n        </text>\n      )}\n    </div>\n  );\n};\n\nconst StreamingMarker = ({ label }: { readonly label: string }) => {\n  const theme = useTheme();\n\n  return (\n    <div\n      style={recipeStyle(MESSAGE_STREAMING, theme)}\n      testId=\"message-streaming\"\n    >\n      <div\n        style={{\n          width: 6,\n          height: 6,\n          borderRadius: theme.radius.full,\n          backgroundColor: theme.colors.primary,\n          flexShrink: 0,\n        }}\n      />\n      <text>{label}</text>\n    </div>\n  );\n};\n\nexport const MessageContent = ({\n  children,\n  reasoning,\n  toolCalls = [],\n  sources = [],\n  streaming = false,\n  streamingLabel = \"Streaming…\",\n  reasoningLabel = \"Reasoning\",\n  sourcesLabel = \"Sources\",\n  className,\n  style,\n  ...rest\n}: MessageContentProps) => {\n  const theme = useTheme();\n  const { variant, align } = useMessage();\n  assertParts(messageContent, children);\n  const calls = listOf(toolCalls);\n  const citations = listOf(sources);\n  const thought = visibleCopy(textOf(reasoning));\n  const streamingWord = accessibleName(textOf(streamingLabel)) ?? \"Streaming…\";\n  const reasoningWord = accessibleName(textOf(reasoningLabel)) ?? \"Reasoning\";\n  const citationsWord = accessibleName(textOf(sourcesLabel)) ?? \"Sources\";\n\n  return (\n    <div\n      {...rest}\n      style={mergeStyles(\n        mergeStyles(recipeStyle(cn(MESSAGE_CONTENT, className), theme), {\n          alignItems: align === \"end\" ? \"flex-end\" : \"flex-start\",\n        }),\n        resolveStyle(style, { variant, align, streaming }),\n      )}\n    >\n      {thought === undefined ? null : (\n        <Reasoning text={thought} label={reasoningWord} />\n      )}\n      {children}\n      {calls.map((call) => (\n        <ToolCall key={call.name} call={call} />\n      ))}\n      {citations.length === 0 ? null : (\n        <div\n          role=\"list\"\n          aria-label={citationsWord}\n          style={recipeStyle(MESSAGE_SOURCES, theme)}\n        >\n          {citations.map((source) => (\n            <div\n              key={source.label}\n              role=\"listitem\"\n              {...(source.href === undefined\n                ? {}\n                : { \"aria-description\": source.href })}\n              style={recipeStyle(MESSAGE_SOURCE, theme)}\n            >\n              <Icon\n                name=\"link\"\n                size=\"xs\"\n                color={theme.colors.mutedForeground}\n              />\n              <text>{source.label}</text>\n            </div>\n          ))}\n        </div>\n      )}\n      {streaming ? <StreamingMarker label={streamingWord} /> : null}\n    </div>\n  );\n};\n\nexport interface MessageActionsProps extends Omit<Props, \"children\"> {\n  readonly onCopy?: () => void;\n\n  readonly onRetry?: () => void;\n\n  readonly onFeedback?: (feedback: \"helpful\" | \"not-helpful\") => void;\n\n  readonly copyLabel?: string;\n\n  readonly retryLabel?: string;\n\n  readonly helpfulLabel?: string;\n\n  readonly notHelpfulLabel?: string;\n\n  readonly className?: string;\n}\n\nexport const MessageActions = ({\n  onCopy,\n  onRetry,\n  onFeedback,\n  copyLabel = \"Copy\",\n  retryLabel = \"Retry\",\n  helpfulLabel = \"Helpful\",\n  notHelpfulLabel = \"Not helpful\",\n  className,\n  style,\n  ...rest\n}: MessageActionsProps) => {\n  const theme = useTheme();\n\n  if (\n    onCopy === undefined &&\n    onRetry === undefined &&\n    onFeedback === undefined\n  ) {\n    return null;\n  }\n\n  return (\n    <div\n      role=\"group\"\n      aria-label=\"Message actions\"\n      {...rest}\n      style={mergeStyles(\n        recipeStyle(cn(MESSAGE_ACTIONS, className), theme),\n        style,\n      )}\n    >\n      {onCopy === undefined ? null : (\n        <MessageAction label={copyLabel} onPress={onCopy} />\n      )}\n      {onRetry === undefined ? null : (\n        <MessageAction label={retryLabel} onPress={onRetry} />\n      )}\n      {onFeedback === undefined ? null : (\n        <>\n          <MessageAction\n            label={helpfulLabel}\n            onPress={() => onFeedback(\"helpful\")}\n          />\n          <MessageAction\n            label={notHelpfulLabel}\n            onPress={() => onFeedback(\"not-helpful\")}\n          />\n        </>\n      )}\n    </div>\n  );\n};\n\nconst MessageAction = ({\n  label,\n  onPress,\n}: {\n  readonly label: string;\n  readonly onPress: () => void;\n}) => (\n  <Button size=\"sm\" variant=\"ghost\" onClick={onPress}>\n    {label}\n  </Button>\n);\n\nexport interface MessageGroupProps extends Omit<Props, \"children\"> {\n  readonly gap?: number;\n\n  readonly className?: string;\n  readonly children?: PartChildren;\n}\n\nexport const MessageGroup = ({\n  gap,\n  className,\n  children,\n  style,\n  ...rest\n}: MessageGroupProps) => {\n  const theme = useTheme();\n  return (\n    <div\n      {...rest}\n      style={mergeStyles(\n        mergeStyles(recipeStyle(cn(MESSAGE_GROUP, className), theme), {\n          gap: gap ?? theme.spacing.sm,\n        }),\n        style,\n      )}\n    >\n      {children}\n    </div>\n  );\n};\n\nconst messageFamily: ReadonlyArray<PartComponent> = [\n  Message,\n  MessageAvatar,\n  MessageHeader,\n  MessageContent,\n  MessageFooter,\n  MessageActions,\n  MessageGroup,\n];\n\nconst messageRoot: PartsContract = {\n  parent: \"Message\",\n  family: messageFamily,\n  accepts: [MessageAvatar, MessageContent],\n  hint: \"Put MessageAvatar and MessageContent directly inside it.\",\n};\n\nconst messageContent: PartsContract = {\n  parent: \"MessageContent\",\n  family: messageFamily,\n  accepts: [MessageHeader, MessageFooter],\n  hint: \"Put MessageHeader and MessageFooter directly inside it; the bubble is a child too.\",\n};\n\nconst messageFooter: PartsContract = {\n  parent: \"MessageFooter\",\n  family: messageFamily,\n  accepts: [MessageActions],\n  hint: \"Put MessageActions directly inside it.\",\n};\n\nexport const messageMeta = {\n  name: \"Message\",\n  tier: 2,\n  status: \"done\",\n  gpuixSupport: \"partial\",\n  limitations: [\n    \"the bubble is a child (`Bubble`), not built by `MessageContent`: reusing a surface on its own is the point of the split\",\n    \"the reasoning panel grows through `Collapsible`'s measured height (`AutoHeight`), so its height is the body's measurement rather than a fixed box\",\n    \"no aria-labelledby or aria-describedby, so a tool call's result and a citation's href are not associated with the row that names them\",\n    \"no clipboard write, so Copy is the consumer's handler; the runtime's own path is a read-only field the user copies\",\n    \"a citation is a labelled row, not a link: there is no anchor element, and opening an href is the consumer's callback surface\",\n    \"a reply restored from storage is read structurally: a slot that is not a list carries nothing, a blank label or sentence reads as its documented words, and a role a release renamed reads as the assistant default\",\n  ],\n  docs: \"docs/message.mdx\",\n  example: \"examples/message.example.tsx\",\n  testFile: \"src/behaviour/message.test.tsx\",\n} as const;\n"
    }
  ],
  "docs": "[Message](https://gpuixui.sudokw.com/components/message) — These are GPUix native components: the files render through GPUix's reconciler against `@gpuix/react`, not the DOM, and need the per-platform `@gpuix/native` binding. The CLI installs the `@gpuixui/*` runtime the components import as registry items, so the copied tree resolves on its own; wrap it in `ThemeProvider` before it renders.",
  "meta": {
    "url": "/components/message",
    "kind": "component",
    "runtime": "gpuix",
    "registry": "gpuixui"
  }
}