{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "gpuix-primitives",
  "type": "registry:lib",
  "title": "Runtime: primitives",
  "description": "The `@gpuixui/primitives` modules the component items import. Installed automatically with the component; not a component itself.",
  "author": "Cyberistic <https://github.com/Cyberistic>",
  "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-tokens.json",
    "https://gpuixui.sudokw.com/gpuix-tw.json"
  ],
  "files": [
    {
      "path": "packages/primitives/src/button.ts",
      "target": "@ui/gpuixui/primitives/button.ts",
      "type": "registry:lib",
      "content": "export { Button } from \"./components/Button.js\";\nexport type { ButtonProps } from \"./components/Button.js\";\n\nexport { IconButton } from \"./components/IconButton.js\";\nexport type { IconButtonProps } from \"./components/IconButton.js\";\n\nexport { actionOnColor } from \"./core/variants.js\";\nexport type { ButtonVariant } from \"./core/variants.js\";\n"
    },
    {
      "path": "packages/primitives/src/components/Box.tsx",
      "target": "@ui/gpuixui/primitives/components/Box.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport type { BoxSpec } from \"../core/layout.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { boxClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, type ElementHostProps } from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface BoxProps extends BoxSpec, ElementHostProps {\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const renderBox = (\n  {\n    style,\n    className,\n    children,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    ...spec\n  }: BoxProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const resolved = recipeTw.classesToStyle(\n    cn(boxClass(spec), className),\n    theme,\n  );\n  return (\n    <div\n      ref={ref}\n      aria-id={controlId}\n      onLayout={onLayout}\n      testId={testId}\n      style={mergeStyle(resolved.style, style)}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport const Box = (props: BoxProps): ReactElement =>\n  renderBox(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/Button.tsx",
      "target": "@ui/gpuixui/primitives/components/Button.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type { EventPayload, KeyEvent } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { reducedMotionOf } from \"../core/motion.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport {\n  busyWord,\n  defaultMessages,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\nimport {\n  decideActivation,\n  isPlainKey,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport { colorRole, controlSize, iconSideOf } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport {\n  variantSpinnerAccent,\n  paintsFilledSurface,\n  type ButtonVariant,\n} from \"../core/variants.js\";\nimport { buttonClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  AttachedGroup,\n  childrenName,\n  composeEvent,\n  composePress,\n  mergeStyle,\n  suppressFeedback,\n  type ActionHostProps,\n  type InteractionProps,\n  type TooltipTriggerProps,\n} from \"./internal.js\";\nimport { controlRing, keepContent } from \"../core/focus.js\";\nimport { Spinner } from \"./Spinner.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport const ToolbarScope = createContext(false);\n\nexport interface ButtonProps\n  extends InteractionProps, TooltipTriggerProps, ActionHostProps {\n  readonly variant?: ButtonVariant;\n\n  readonly size?: ControlSize;\n\n  readonly square?: boolean;\n  readonly fullWidth?: boolean;\n  readonly disabled?: boolean;\n\n  readonly loading?: boolean;\n\n  readonly loadingLabel?: string;\n\n  readonly reducedMotion?: boolean;\n  readonly leading?: ReactNode;\n  readonly trailing?: ReactNode;\n\n  readonly label?: string;\n\n  readonly onActivate?: (event: EventPayload) => void;\n\n  readonly style?: StateStyle<ButtonState>;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n\n  readonly tabIndex?: number;\n}\n\nexport interface ButtonFocus {\n  readonly focused: boolean;\n  readonly onFocus?: () => void;\n  readonly onBlur?: () => void;\n}\n\nexport interface ButtonState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n}\n\nconst isBusy = (props: {\n  readonly disabled?: boolean;\n  readonly loading?: boolean;\n}): boolean => props.disabled === true || props.loading === true;\n\nexport const renderButton = (\n  props: ButtonProps,\n  theme: PrimitiveTheme,\n  focus: ButtonFocus,\n  attached = false,\n  messages: PrimitiveMessages = defaultMessages,\n\n  inToolbar = false,\n): ReactElement => {\n  const {\n    variant = \"primary\",\n    size: requestedSize,\n    square,\n    fullWidth,\n    disabled,\n    loading,\n    loadingLabel,\n    reducedMotion,\n    leading,\n    trailing,\n    label,\n    onActivate,\n    style: callerStyle,\n    className,\n    children,\n    testId,\n    tabIndex,\n    onClick,\n    onKeyDown,\n    onKeyUp,\n    onFocus,\n    onBlur,\n    onMouseEnter,\n    onMouseLeave,\n    \"aria-description\": description,\n\n    ...host\n  } = props;\n  const size = controlSize(requestedSize);\n  const busy = isBusy({ disabled, loading });\n\n  const loadingWord = loadingLabel ?? busyWord(messages.loading);\n  const activate = (event: EventPayload): void => {\n    if (busy) return;\n    onActivate?.(event);\n  };\n  const onKey =\n    (phase: \"down\" | \"up\") =>\n    (event: KeyEvent): void => {\n      if (!isPlainKey(event.modifiers)) return;\n      if (swallowKeyDefault(event.key ?? \"\", phase)) event.preventDefault();\n      if (decideActivation(event.key ?? \"\", phase, event.isHeld === true))\n        activate(event);\n    };\n\n  const resolved = recipeTw.classesToStyle(\n    cn(\n      buttonClass(variant, size, {\n        square,\n        fullWidth,\n      }),\n      className,\n    ),\n    theme,\n  );\n  const base =\n    disabled === true\n      ? suppressFeedback(mergeStyle(resolved.style, resolved.disabled))\n      : theme.interaction === \"appkit\" && inToolbar\n        ? mergeStyle(resolved.style, {\n            hover: { backgroundColor: colorRole(theme, \"muted\") },\n          })\n        : resolved.style;\n  const own = mergeStyle(\n    paintsFilledSurface(variant) ? mergeStyle(base, keepContent) : base,\n    controlRing(theme, { disabled }),\n  );\n  const shape = attached ? mergeStyle(own, { borderRadius: 0 }) : own;\n  const style = mergeStyle(\n    shape,\n    resolveStyle(callerStyle, {\n      disabled: busy,\n      focused: focus.focused,\n    }),\n  );\n\n  return (\n    <div\n      {...host}\n      role=\"button\"\n      aria-label={accessibleName(label) ?? childrenName(children)}\n      aria-description={\n        loading === true\n          ? accessibleName(loadingWord)\n          : accessibleName(description)\n      }\n      tabIndex={tabIndex ?? (disabled === true ? -1 : 0)}\n      testId={testId}\n      onClick={composePress(busy, onClick, activate)}\n      onMouseEnter={onMouseEnter}\n      onMouseLeave={onMouseLeave}\n      onFocus={composeEvent<EventPayload>(onFocus, focus.onFocus)}\n      onBlur={composeEvent<EventPayload>(onBlur, focus.onBlur)}\n      onKeyDown={composeEvent<KeyEvent>(onKey(\"down\"), onKeyDown)}\n      onKeyUp={composeEvent<KeyEvent>(onKey(\"up\"), onKeyUp)}\n      style={style}\n    >\n      {loading === true ? (\n        <Spinner\n          size={iconSideOf(theme, \"sm\")}\n          accent={variantSpinnerAccent(variant, disabled)}\n          label={loadingWord}\n          reducedMotion={reducedMotion}\n        />\n      ) : (\n        leading\n      )}\n      {children}\n      {trailing}\n    </div>\n  );\n};\n\nexport const Button = (props: ButtonProps): ReactElement => {\n  const theme = useTheme();\n  const [focused, setFocused] = useState(false);\n\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  return renderButton(\n    { ...props, reducedMotion },\n    theme,\n    {\n      focused,\n      onFocus: () => setFocused(true),\n      onBlur: () => setFocused(false),\n    },\n    useContext(AttachedGroup),\n    useMessages(),\n    useContext(ToolbarScope),\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/ButtonGroup.tsx",
      "target": "@ui/gpuixui/primitives/components/ButtonGroup.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport * as Arr from \"effect/Array\";\nimport * as P from \"effect/Predicate\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport { spaceStep } from \"../core/theme.js\";\nimport type { PrimitiveTheme, SpaceStep } from \"../core/theme.js\";\nimport { buttonGroupClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  mergeStyle,\n  AttachedGroup,\n  type ElementHostProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport const BUTTON_GROUP_MODES = [\"attached\", \"separated\"] as const;\nexport type ButtonGroupMode = (typeof BUTTON_GROUP_MODES)[number];\n\nconst MODE_NAMES: ReadonlyArray<string> = BUTTON_GROUP_MODES;\n\nexport const isButtonGroupMode = (value: unknown): value is ButtonGroupMode =>\n  P.isString(value) && Arr.contains(MODE_NAMES, value);\n\nexport const buttonGroupModeOf = (value: unknown): ButtonGroupMode =>\n  isButtonGroupMode(value) ? value : \"attached\";\n\nexport const BUTTON_GROUP_ORIENTATIONS = [\"horizontal\", \"vertical\"] as const;\nexport type ButtonGroupOrientation = (typeof BUTTON_GROUP_ORIENTATIONS)[number];\n\nconst ORIENTATION_NAMES: ReadonlyArray<string> = BUTTON_GROUP_ORIENTATIONS;\n\nexport const isButtonGroupOrientation = (\n  value: unknown,\n): value is ButtonGroupOrientation =>\n  P.isString(value) && Arr.contains(ORIENTATION_NAMES, value);\n\nexport const buttonGroupOrientationOf = (\n  value: unknown,\n): ButtonGroupOrientation =>\n  isButtonGroupOrientation(value) ? value : \"horizontal\";\n\nexport interface ButtonGroupProps extends ElementHostProps {\n  readonly mode?: ButtonGroupMode;\n\n  readonly orientation?: ButtonGroupOrientation;\n\n  readonly gap?: SpaceStep | null;\n\n  readonly label?: string;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const renderButtonGroup = (\n  {\n    mode,\n    orientation,\n    gap = \"sm\",\n    label,\n    style,\n    className,\n    children,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n  }: ButtonGroupProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const shape = buttonGroupModeOf(mode);\n  const axis = buttonGroupOrientationOf(orientation);\n  const distance = spaceStep(theme, gap) ?? \"sm\";\n  const resolved = recipeTw.classesToStyle(\n    cn(buttonGroupClass(shape, distance, axis), className),\n    theme,\n  );\n  return (\n    <div\n      ref={ref}\n      role=\"group\"\n      aria-label={accessibleName(label)}\n      aria-id={controlId}\n      onLayout={onLayout}\n      testId={testId}\n      style={mergeStyle(resolved.style, style)}\n    >\n      <AttachedGroup.Provider value={shape === \"attached\"}>\n        {children}\n      </AttachedGroup.Provider>\n    </div>\n  );\n};\n\nexport const ButtonGroup = (props: ButtonGroupProps): ReactElement =>\n  renderButtonGroup(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/Checkbox.tsx",
      "target": "@ui/gpuixui/primitives/components/Checkbox.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type { EventPayload, KeyEvent, StyleDesc } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport { Icon } from \"../../icons/index.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport {\n  decideActivation,\n  isPlainKey,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport {\n  defaultMessages,\n  selectionWord,\n  type PrimitiveMessages,\n  type SelectionWords,\n} from \"../core/messages.js\";\nimport {\n  selectionMarkColor,\n  selectionTargetStyle,\n  type SelectionAccent,\n  type SelectionSpec,\n  type SelectionState,\n} from \"../core/selection.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport { controlSize, iconSideOf } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { selectionBoxClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  checkedChange,\n  composeEvent,\n  composePress,\n  decorative,\n  mergeStyle,\n  suppressSelectionFeedback,\n  useControlState,\n  useFocus,\n  type CheckedChangeProps,\n  type FieldHostProps,\n  type FocusState,\n  type HelpProps,\n  type InteractionProps,\n  type TooltipTriggerProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\n\ninterface CheckboxPart {\n  readonly spec: SelectionSpec;\n  readonly theme: PrimitiveTheme;\n}\n\nconst CheckboxContext = createContext<CheckboxPart | null>(null);\n\nexport interface CheckboxIndicatorProps {\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n  readonly className?: string;\n}\n\nexport interface CheckboxProps\n  extends\n    CheckedChangeProps,\n    InteractionProps,\n    TooltipTriggerProps,\n    FieldHostProps,\n    HelpProps {\n  readonly checked?: boolean;\n  readonly defaultChecked?: boolean;\n\n  readonly children?: ReactNode;\n\n  readonly indeterminate?: boolean;\n  readonly disabled?: boolean;\n  readonly invalid?: boolean;\n\n  readonly size?: ControlSize;\n\n  readonly accent?: SelectionAccent;\n\n  readonly label?: string;\n\n  readonly style?: StateStyle<CheckboxState>;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport interface CheckboxState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly checked: boolean;\n  readonly indeterminate: boolean;\n  readonly invalid: boolean;\n}\n\nexport const selectionValueText = (\n  state: SelectionState,\n  words: SelectionWords = defaultMessages.selection,\n): string => selectionWord(state, words);\n\nexport const checkboxState = (props: CheckboxProps): SelectionState =>\n  props.indeterminate === true\n    ? \"indeterminate\"\n    : props.checked\n      ? \"checked\"\n      : \"unchecked\";\n\nconst markName = (state: SelectionState): \"check\" | \"minus\" =>\n  state === \"indeterminate\" ? \"minus\" : \"check\";\n\nexport const renderCheckboxIndicator = (\n  props: CheckboxIndicatorProps,\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const region: StyleDesc = {\n    display: \"flex\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    pointerEvents: \"none\",\n  };\n  const mark = selectionMarkColor(spec, theme);\n  return (\n    <div\n      style={mergeStyle(\n        mergeStyle(\n          region,\n          recipeTw.classesToStyle(props.className, theme).style,\n        ),\n        props.style,\n      )}\n    >\n      {props.children ??\n        (mark === undefined ? null : (\n          <Icon\n            name={markName(spec.state)}\n            size={iconSideOf(theme, \"sm\")}\n            color={mark}\n          />\n        ))}\n    </div>\n  );\n};\n\nexport const CheckboxIndicator = (\n  props: CheckboxIndicatorProps,\n): ReactElement => {\n  const part = useContext(CheckboxContext);\n  if (part === null) {\n    throw new Error(\"CheckboxIndicator must be used inside Checkbox\");\n  }\n  return renderCheckboxIndicator(props, part.spec, part.theme);\n};\n\nexport const renderCheckbox = (\n  props: CheckboxProps,\n  theme: PrimitiveTheme,\n  state: ValueState<boolean>,\n  focus: FocusState,\n  onValueChange: (next: boolean) => void,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const current: SelectionState =\n    props.indeterminate === true\n      ? \"indeterminate\"\n      : state.current\n        ? \"checked\"\n        : \"unchecked\";\n  const spec: SelectionSpec = {\n    state: current,\n    size: controlSize(props.size),\n    accent: props.accent ?? \"primary\",\n    shape: \"square\",\n    invalid: props.invalid,\n    disabled: props.disabled,\n  };\n  const toggle = (): void => {\n    if (props.disabled === true) return;\n    onValueChange(!state.current);\n  };\n  const onKey =\n    (phase: \"down\" | \"up\") =>\n    (event: KeyEvent): void => {\n      if (!isPlainKey(event.modifiers)) return;\n      if (swallowKeyDefault(event.key ?? \"\", phase)) event.preventDefault();\n      if (decideActivation(event.key ?? \"\", phase, event.isHeld === true))\n        toggle();\n    };\n  const resolved = recipeTw.classesToStyle(\n    cn(selectionBoxClass(spec), props.className),\n    theme,\n  );\n  const drawn =\n    spec.disabled === true\n      ? suppressSelectionFeedback(mergeStyle(resolved.style, resolved.disabled))\n      : resolved.style;\n  const drawing = decorative(\n    mergeStyle(\n      drawn,\n      resolveStyle(props.style, {\n        disabled: spec.disabled === true,\n        focused: focus.focused,\n        checked: current === \"checked\",\n        indeterminate: current === \"indeterminate\",\n        invalid: spec.invalid === true,\n      }),\n    ),\n  );\n  const target = mergeStyle(\n    selectionTargetStyle(spec, theme),\n    controlRing(theme, { disabled: spec.disabled, invalid: spec.invalid }),\n  );\n  return (\n    <div\n      ref={props.ref}\n      role=\"checkbox\"\n      aria-label={accessibleName(props.label)}\n      aria-description={\n        accessibleName(props.description) ??\n        accessibleName(props[\"aria-description\"])\n      }\n      aria-id={props[\"aria-id\"]}\n      aria-valuetext={selectionValueText(current, messages.selection)}\n      tabIndex={props.disabled === true ? undefined : 0}\n      testId={props.testId}\n      onLayout={props.onLayout}\n      onClick={composePress(props.disabled === true, props.onClick, toggle)}\n      onMouseEnter={props.onMouseEnter}\n      onMouseLeave={props.onMouseLeave}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, onKey(\"down\"))}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, onKey(\"up\"))}\n      onFocus={composeEvent<EventPayload>(props.onFocus, focus.onFocus)}\n      onBlur={composeEvent<EventPayload>(props.onBlur, focus.onBlur)}\n      style={target}\n    >\n      <div style={drawing}>\n        {current === \"unchecked\" ? null : (\n          <CheckboxContext.Provider value={{ spec, theme }}>\n            {renderCheckboxIndicator({ children: props.children }, spec, theme)}\n          </CheckboxContext.Provider>\n        )}\n      </div>\n    </div>\n  );\n};\n\nexport const Checkbox = (props: CheckboxProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const focus = useFocus();\n  const state = useControlState(\n    props.checked,\n    props.defaultChecked ?? false,\n    checkedChange(props),\n  );\n  return renderCheckbox(props, theme, state.value, focus, state.set, messages);\n};\n"
    },
    {
      "path": "packages/primitives/src/components/CheckboxGroup.tsx",
      "target": "@ui/gpuixui/primitives/components/CheckboxGroup.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport type { EventPayload, KeyEvent } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport {\n  decideAbsolute,\n  decideActivation,\n  decideMove,\n  isPlainKey,\n  orientationOf,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport { rovingIndex } from \"../core/roving.js\";\nimport {\n  toggleSelection,\n  type SelectionAccent,\n  type SelectionSpec,\n  type SelectionState,\n} from \"../core/selection.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { storedTextList } from \"../core/stored.js\";\nimport { selectionBoxClass, stackClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { renderCheckboxIndicator, selectionValueText } from \"./Checkbox.js\";\nimport {\n  focusedOption,\n  groupOptions,\n  optionBody,\n  optionDisabled,\n  optionHelp,\n  optionItems,\n  optionName,\n  optionRowStyle,\n  useRovingFocus,\n  useTypeAhead,\n  type GroupFocus,\n  type GroupHandlers,\n  type GroupKeys,\n  type GroupItemProps,\n  type GroupOption,\n} from \"./group.js\";\nimport {\n  composeEvent,\n  composePress,\n  decorative,\n  mergeRefs,\n  mergeStyle,\n  suppressSelectionFeedback,\n  useControlState,\n  valueChange,\n  type FieldHostProps,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\n\nexport type CheckboxGroupOption = GroupOption;\n\nexport type CheckboxGroupItemProps = GroupItemProps;\n\nexport const CheckboxGroupItem = (_props: CheckboxGroupItemProps): null => null;\n\nexport const checkboxGroupItems = (props: {\n  readonly options?: ReadonlyArray<CheckboxGroupOption>;\n  readonly children?: ReactNode;\n}): ReadonlyArray<CheckboxGroupOption> =>\n  props.options === undefined || props.options === null\n    ? optionItems(props.children, CheckboxGroupItem)\n    : groupOptions(props.options);\n\nexport interface CheckboxGroupProps\n  extends\n    ValueChangeProps<ReadonlyArray<string>>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly options?: ReadonlyArray<CheckboxGroupOption>;\n\n  readonly children?: ReactNode;\n\n  readonly value?: ReadonlyArray<string>;\n  readonly defaultValue?: ReadonlyArray<string>;\n\n  readonly orientation?: \"vertical\" | \"horizontal\";\n\n  readonly size?: ControlSize;\n\n  readonly accent?: SelectionAccent;\n\n  readonly disabled?: boolean;\n\n  readonly invalid?: boolean;\n\n  readonly label?: string;\n\n  readonly style?: StateStyle<CheckboxGroupState>;\n\n  readonly itemStyle?: StateStyle<CheckboxItemState>;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n\n  readonly itemTestId?: (value: string) => string;\n}\n\nexport interface CheckboxGroupState {\n  readonly disabled: boolean;\n\n  readonly focused: boolean;\n}\n\nexport interface CheckboxItemState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly checked: boolean;\n}\n\nexport interface CheckboxGroupHandlers extends GroupHandlers, GroupKeys {\n  readonly onToggle: (value: string) => void;\n}\n\nexport const renderCheckboxGroup = (\n  props: CheckboxGroupProps,\n  theme: PrimitiveTheme,\n  values: ReadonlyArray<string>,\n  preferredIndex: Option.Option<number>,\n  focus: GroupFocus,\n  handlers: CheckboxGroupHandlers,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const orientation = orientationOf(props.orientation, \"vertical\");\n  const { accent = \"primary\" } = props;\n  const size = controlSize(props.size);\n  const options = checkboxGroupItems(props);\n  const count = Arr.length(options);\n  const isDisabled = optionDisabled(options, props.disabled);\n\n  const focused = focusedOption(count, focus.focusedIndex);\n\n  const activeIndex = rovingIndex(count, preferredIndex, isDisabled);\n  const toggle = (index: number): void => {\n    const option = options[index];\n    if (option === undefined || isDisabled(index)) return;\n    handlers.onToggle(option.value);\n  };\n  const focusMove = (index: number): true => {\n    handlers.onFocusMove(index);\n    return true;\n  };\n  const move = (key: string): boolean =>\n    Option.match(activeIndex, {\n      onNone: () => false,\n      onSome: (index) =>\n        Option.match(decideMove(index, count, key, orientation, isDisabled), {\n          onNone: () => false,\n          onSome: focusMove,\n        }),\n    });\n  const jump = (key: string): boolean =>\n    Option.match(decideAbsolute(count, key, isDisabled), {\n      onNone: () => false,\n      onSome: focusMove,\n    });\n  const activate = (): void => {\n    Option.match(activeIndex, { onNone: () => {}, onSome: toggle });\n  };\n  const onKeyDown = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    const key = event.key ?? \"\";\n    if (swallowKeyDefault(key, \"down\")) event.preventDefault();\n    if (move(key) || jump(key)) return;\n\n    const typed = Option.flatMap(\n      Option.all([Option.fromNullishOr(handlers.typeAhead), activeIndex]),\n      ([match, from]) => match(from, key),\n    );\n    if (Option.match(typed, { onNone: () => false, onSome: focusMove })) return;\n    if (decideActivation(key, \"down\", event.isHeld === true)) activate();\n  };\n\n  const onKeyUp = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    if (decideActivation(event.key ?? \"\", \"up\", false)) activate();\n  };\n  const group = mergeStyle(\n    recipeTw.classesToStyle(\n      cn(\n        stackClass({\n          direction: orientation === \"horizontal\" ? \"row\" : \"column\",\n          gap: \"sm\",\n        }),\n        props.className,\n      ),\n      theme,\n    ).style,\n    resolveStyle(props.style, {\n      disabled: props.disabled === true,\n      focused: Option.isSome(focused),\n    }),\n  );\n  return (\n    <div\n      role=\"group\"\n      aria-label={accessibleName(props.label)}\n      aria-description={accessibleName(props.description)}\n      testId={props.testId}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, onKeyDown)}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, onKeyUp)}\n      style={group}\n    >\n      {Arr.map(options, (option, index) => {\n        const disabled = isDisabled(index);\n        const current: SelectionState = Arr.contains(values, option.value)\n          ? \"checked\"\n          : \"unchecked\";\n        const spec: SelectionSpec = {\n          state: current,\n          size,\n          accent,\n          shape: \"square\",\n          disabled,\n          invalid: props.invalid,\n        };\n        const resolved = recipeTw.classesToStyle(\n          selectionBoxClass(spec),\n          theme,\n        );\n        const box = decorative(\n          disabled\n            ? suppressSelectionFeedback(\n                mergeStyle(resolved.style, resolved.disabled),\n              )\n            : resolved.style,\n        );\n        const row = mergeStyle(\n          optionRowStyle({ disabled, invalid: spec.invalid }, theme),\n          resolveStyle(props.itemStyle, {\n            disabled,\n            focused: Option.contains(focused, index),\n            checked: current === \"checked\",\n          }),\n        );\n\n        const addressed = Option.contains(activeIndex, index);\n        const register = handlers.registerItem(index);\n        return (\n          <div\n            key={option.value}\n            ref={addressed ? mergeRefs(register, props.ref) : register}\n            role=\"checkbox\"\n            aria-label={optionName(option)}\n            aria-description={optionHelp(option)}\n            aria-id={addressed ? props[\"aria-id\"] : undefined}\n            aria-valuetext={selectionValueText(current, messages.selection)}\n            testId={props.itemTestId?.(option.value)}\n            tabIndex={\n              disabled\n                ? undefined\n                : Option.contains(activeIndex, index)\n                  ? 0\n                  : -1\n            }\n            onLayout={addressed ? props.onLayout : undefined}\n            onClick={composePress(disabled, props.onClick, () => {\n              toggle(index);\n            })}\n            onFocus={composeEvent<EventPayload>(props.onFocus, () =>\n              focus.onFocus(index),\n            )}\n            onBlur={composeEvent<EventPayload>(props.onBlur, () =>\n              focus.onBlur(index),\n            )}\n            style={row}\n          >\n            <div style={box}>\n              {current === \"unchecked\"\n                ? null\n                : renderCheckboxIndicator({}, spec, theme)}\n            </div>\n            {optionBody(option, disabled, theme)}\n          </div>\n        );\n      })}\n    </div>\n  );\n};\n\nexport const CheckboxGroup = (props: CheckboxGroupProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const focus = useRovingFocus();\n  const selected = useControlState<ReadonlyArray<string>>(\n    Option.getOrUndefined(storedTextList(props.value)),\n    Option.getOrElse(storedTextList(props.defaultValue), () => []),\n    valueChange(props),\n  );\n  const options = checkboxGroupItems(props);\n  const typeAhead = useTypeAhead(\n    options,\n    optionDisabled(options, props.disabled),\n  );\n  const preferred: Option.Option<number> = Option.firstSomeOf([\n    focus.focusedIndex,\n    Arr.findFirstIndex(options, (option) =>\n      Arr.contains(selected.value.current, option.value),\n    ),\n  ]);\n  return renderCheckboxGroup(\n    props,\n    theme,\n    selected.value.current,\n    preferred,\n    focus,\n    {\n      onToggle: (value) =>\n        selected.set(\n          toggleSelection(\"multiple\", selected.value.current, value),\n        ),\n      onFocusMove: focus.onFocusMove,\n      registerItem: focus.registerItem,\n      typeAhead,\n    },\n    messages,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/IconButton.tsx",
      "target": "@ui/gpuixui/primitives/components/IconButton.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport { useContext, type ReactElement, type ReactNode } from \"react\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport type { EventPayload } from \"@gpuix/react\";\nimport type { StateStyle } from \"@gpuix/react/floating\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport type { ButtonVariant } from \"../core/variants.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport { reducedMotionOf } from \"../core/motion.js\";\nimport {\n  renderButton,\n  ToolbarScope,\n  type ButtonFocus,\n  type ButtonProps,\n  type ButtonState,\n} from \"./Button.js\";\nimport {\n  AttachedGroup,\n  useFocus,\n  type ActionHostProps,\n  type InteractionProps,\n  type TooltipTriggerProps,\n} from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface IconButtonProps\n  extends InteractionProps, TooltipTriggerProps, ActionHostProps {\n  readonly label: string;\n\n  readonly icon: ReactNode;\n\n  readonly variant?: ButtonVariant;\n\n  readonly size?: ControlSize;\n  readonly disabled?: boolean;\n\n  readonly loading?: boolean;\n\n  readonly loadingLabel?: string;\n\n  readonly reducedMotion?: boolean;\n\n  readonly onActivate?: (event: EventPayload) => void;\n\n  readonly style?: StateStyle<ButtonState>;\n\n  readonly className?: string;\n  readonly testId?: string;\n\n  readonly tabIndex?: number;\n}\n\nexport const iconButtonProps = (props: IconButtonProps): ButtonProps => {\n  const {\n    variant,\n    size,\n    disabled,\n    loading,\n    loadingLabel,\n    reducedMotion,\n    label,\n    icon,\n    onActivate,\n    onClick,\n    onMouseEnter,\n    onMouseLeave,\n    \"aria-description\": description,\n    onKeyDown,\n    onKeyUp,\n    onFocus,\n    onBlur,\n    style,\n    className,\n    testId,\n    tabIndex,\n\n    ...host\n  } = props;\n  return {\n    ...host,\n    variant: variant ?? \"ghost\",\n    size: size === undefined ? undefined : controlSize(size),\n    square: true,\n    disabled,\n    loading,\n    loadingLabel,\n    reducedMotion,\n    label,\n    leading: icon,\n    onActivate,\n    onClick,\n    onMouseEnter,\n    onMouseLeave,\n    \"aria-description\": description,\n    onKeyDown,\n    onKeyUp,\n    onFocus,\n    onBlur,\n    style,\n    className,\n    testId,\n    tabIndex,\n  };\n};\n\nexport const renderIconButton = (\n  props: IconButtonProps,\n  theme: PrimitiveTheme,\n  focus: ButtonFocus,\n  attached = false,\n  messages: PrimitiveMessages = defaultMessages,\n  inToolbar = false,\n): ReactElement =>\n  renderButton(\n    iconButtonProps(props),\n    theme,\n    focus,\n    attached,\n    messages,\n    inToolbar,\n  );\n\nexport const IconButton = (props: IconButtonProps): ReactElement => {\n  const theme = useTheme();\n  const focus = useFocus();\n\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  return renderIconButton(\n    { ...props, reducedMotion },\n    theme,\n    focus,\n    useContext(AttachedGroup),\n    useMessages(),\n    useContext(ToolbarScope),\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Input.tsx",
      "target": "@ui/gpuixui/primitives/components/Input.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement } from \"react\";\nimport type { EventPayload, StyleDesc } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport * as Option from \"effect/Option\";\nimport { accessibleName, fieldHelp } from \"../core/a11y.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { nativeTextTheme } from \"../core/style.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { controlRing, keepContent } from \"../core/focus.js\";\nimport { fieldClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { fieldHint, renderFieldHint } from \"./hint.js\";\nimport {\n  composeEvent,\n  mergeStyle,\n  suppressFeedback,\n  useControlState,\n  useFocus,\n  valueChange,\n  type FieldHostProps,\n  type FocusState,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface FieldState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly invalid: boolean;\n}\n\nexport interface FieldProps\n  extends\n    ValueChangeProps<string>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly value?: string;\n\n  readonly defaultValue?: string;\n\n  readonly onSubmit?: (event: EventPayload) => void;\n  readonly placeholder?: string;\n  readonly readOnly?: boolean;\n\n  readonly disabled?: boolean;\n\n  readonly autoFocus?: boolean;\n\n  readonly invalid?: boolean;\n\n  readonly size?: ControlSize;\n  readonly fullWidth?: boolean;\n\n  readonly label?: string;\n\n  readonly tabIndex?: number;\n\n  readonly style?: StateStyle<FieldState>;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const fieldChrome = (\n  { invalid, disabled, fullWidth, style, className, size }: FieldProps,\n  theme: PrimitiveTheme,\n  focused: boolean,\n): StyleDesc => {\n  const resolved = recipeTw.classesToStyle(\n    cn(fieldClass({ size: controlSize(size), invalid, fullWidth }), className),\n    theme,\n  );\n  let base = resolved.style;\n  if (invalid === true) base = mergeStyle(base, resolved.invalid);\n  if (disabled === true) {\n    base = mergeStyle(base, resolved.disabled);\n    base = suppressFeedback(base);\n  }\n  const ring = controlRing(theme, { disabled, invalid });\n  return mergeStyle(\n    mergeStyle(mergeStyle(base, keepContent), ring),\n    resolveStyle(style, {\n      disabled: disabled === true,\n      focused,\n      invalid: invalid === true,\n    }),\n  );\n};\n\nexport const fieldFrame: StyleDesc = { position: \"relative\" };\n\nexport const fieldHostProps = (\n  props: FieldProps,\n  theme: PrimitiveTheme,\n  state: ValueState<string>,\n  focus: FocusState,\n  onValueChange: (value: string) => void,\n) => {\n  const { ref, \"aria-id\": controlId, onLayout, tabIndex } = props;\n  return {\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    value: state.current,\n\n    readOnly: props.readOnly === true || props.disabled === true,\n    autoFocus: props.disabled === true ? undefined : props.autoFocus,\n\n    tabIndex: props.disabled === true ? -1 : tabIndex,\n    theme: nativeTextTheme(theme),\n    testId: props.testId,\n    \"aria-label\": accessibleName(props.label),\n    \"aria-description\": fieldHelp(props, state.current),\n    style: fieldChrome(props, theme, focus.focused),\n    onChange: (event: EventPayload) => {\n      if (props.disabled !== true) onValueChange(event.value ?? \"\");\n    },\n    onSubmit: props.disabled === true ? undefined : props.onSubmit,\n    onClick: props.onClick,\n    onKeyDown: props.onKeyDown,\n    onKeyUp: props.onKeyUp,\n    onFocus: composeEvent<EventPayload>(props.onFocus, focus.onFocus),\n    onBlur: composeEvent<EventPayload>(props.onBlur, focus.onBlur),\n  };\n};\n\nexport const renderInput = (\n  props: FieldProps,\n  theme: PrimitiveTheme,\n  state: ValueState<string>,\n  focus: FocusState,\n  onValueChange: (value: string) => void,\n): ReactElement => (\n  <input {...fieldHostProps(props, theme, state, focus, onValueChange)} />\n);\n\nexport type InputProps = FieldProps;\n\nexport const Input = (props: InputProps): ReactElement => {\n  const theme = useTheme();\n  const focus = useFocus();\n  const field = useControlState(\n    props.value,\n    props.defaultValue ?? \"\",\n    valueChange(props),\n  );\n  const hint = fieldHint(props, theme, field.value.current, {\n    size: controlSize(props.size),\n    align: \"center\",\n    origin: \"field\",\n  });\n  return (\n    <div style={fieldFrame}>\n      {renderInput(props, theme, field.value, focus, field.set)}\n      {Option.match(hint, {\n        onNone: () => null,\n        onSome: (hint) => renderFieldHint(hint, props.testId),\n      })}\n    </div>\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Label.tsx",
      "target": "@ui/gpuixui/primitives/components/Label.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode, RefObject } from \"react\";\nimport type { PublicInstance, StyleDesc } from \"@gpuix/react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport { visibleCopy } from \"../core/a11y.js\";\nimport { Stack } from \"./Stack.js\";\nimport { Text } from \"./Text.js\";\n\nexport interface LabelProps {\n  readonly target?: RefObject<PublicInstance | null>;\n\n  readonly disabled?: boolean;\n\n  readonly required?: boolean;\n\n  readonly invalid?: boolean;\n\n  readonly description?: string;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport interface ControlName {\n  readonly label: string;\n\n  readonly description?: string;\n}\n\nexport const labelledBy = (name: string, description?: string): ControlName =>\n  description === undefined ? { label: name } : { label: name, description };\n\nconst PRESS_FRAME: StyleDesc = {\n  display: \"flex\",\n  flexDirection: \"column\",\n  width: \"100%\",\n  cursor: \"pointer\",\n};\n\nexport const renderLabel = (\n  {\n    required,\n    invalid,\n    disabled,\n    description,\n    style,\n    className,\n    children,\n    testId,\n  }: LabelProps,\n  onPress?: () => void,\n): ReactElement => {\n  const pressable = onPress !== undefined && disabled !== true;\n\n  const help = visibleCopy(description);\n  const label = (\n    <Stack\n      direction=\"column\"\n      gap=\"2xs\"\n      fullWidth\n      className={className}\n      style={style}\n      testId={testId}\n    >\n      <Stack direction=\"row\" gap=\"2xs\" align=\"baseline\">\n        <Text role=\"label\" tone={invalid === true ? \"destructive\" : \"default\"}>\n          {children}\n        </Text>\n        {required === true ? (\n          <Text role=\"label\" tone=\"destructive\">\n            *\n          </Text>\n        ) : null}\n      </Stack>\n      {help === undefined ? null : (\n        <Text role=\"caption\" tone=\"muted\">\n          {help}\n        </Text>\n      )}\n    </Stack>\n  );\n  return pressable ? (\n    <div style={PRESS_FRAME} onClick={onPress}>\n      {label}\n    </div>\n  ) : (\n    label\n  );\n};\n\nexport const Label = (props: LabelProps): ReactElement => {\n  const { renderer } = useGpuix();\n  const { target } = props;\n  return renderLabel(\n    props,\n    target === undefined\n      ? undefined\n      : () => {\n          const instance = target.current;\n          if (instance === null || renderer === null) return;\n\n          renderer.focusElement?.(instance.id);\n        },\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/MessagesProvider.tsx",
      "target": "@ui/gpuixui/primitives/components/MessagesProvider.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport {\n  defaultMessages,\n  resolveMessages,\n  type MessagesSource,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\n\nconst MessagesContext = createContext<PrimitiveMessages>(defaultMessages);\n\nexport interface MessagesProviderProps {\n  readonly messages?: MessagesSource | null;\n  readonly children: ReactNode;\n}\n\nexport const MessagesProvider = ({\n  messages,\n  children,\n}: MessagesProviderProps): ReactElement => {\n  const inherited = useContext(MessagesContext);\n  return (\n    <MessagesContext.Provider value={resolveMessages(messages, inherited)}>\n      {children}\n    </MessagesContext.Provider>\n  );\n};\n\nexport const useMessages = (): PrimitiveMessages => useContext(MessagesContext);\n"
    },
    {
      "path": "packages/primitives/src/components/Meter.tsx",
      "target": "@ui/gpuixui/primitives/components/Meter.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport * as Option from \"effect/Option\";\nimport {\n  accessibleName,\n  captionCopy,\n  spokenText,\n  visibleCopy,\n} from \"../core/a11y.js\";\nimport {\n  progressSizeOf,\n  progressToneOf,\n  type ProgressSize,\n  type ProgressTone,\n} from \"../core/style.js\";\nimport { clampValue, storedRange, valueFraction } from \"../core/value.js\";\nimport { spaceValue } from \"../core/theme.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { progressFillClass, progressTrackClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, type ElementHostProps } from \"./internal.js\";\nimport { Text } from \"./Text.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface MeterProps extends ElementHostProps {\n  readonly value: number;\n\n  readonly min?: number;\n\n  readonly max?: number;\n\n  readonly tone?: ProgressTone;\n\n  readonly size?: ProgressSize;\n\n  readonly label?: string;\n\n  readonly formatValue?: (value: number) => string;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport interface MeterPart {\n  readonly theme: PrimitiveTheme;\n\n  readonly value: number;\n\n  readonly fraction: number;\n  readonly tone: ProgressTone;\n  readonly size: ProgressSize;\n  readonly label: string | undefined;\n\n  readonly valueText: string;\n}\n\nexport const meterPart = (\n  props: MeterProps,\n  theme: PrimitiveTheme,\n): MeterPart => {\n  const range = storedRange(props, {\n    min: 0,\n    max: 100,\n    step: 0,\n  });\n  const value = clampValue(props.value, range);\n  return {\n    theme,\n    value,\n    fraction: valueFraction(value, range),\n    tone: progressToneOf(props.tone),\n    size: progressSizeOf(props.size),\n\n    label: visibleCopy(props.label),\n\n    valueText: spokenText(\n      Option.fromNullishOr(props.formatValue?.(value)),\n      String(value),\n    ),\n  };\n};\n\nconst MeterPartContext = createContext<MeterPart | null>(null);\n\nconst useMeterPart = (name: string): MeterPart => {\n  const part = useContext(MeterPartContext);\n  if (part === null) {\n    throw new Error(`${name} must be used inside Meter`);\n  }\n  return part;\n};\n\nconst frameStyle = (theme: PrimitiveTheme): StyleDesc => ({\n  width: \"100%\",\n  display: \"flex\",\n  flexDirection: \"column\",\n  gap: spaceValue(theme, \"2xs\"),\n});\n\nexport interface MeterTrackProps {\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderMeterTrack = (\n  { children, className, style, testId }: MeterTrackProps,\n  part: MeterPart,\n): ReactElement => (\n  <div\n    testId={testId}\n    style={mergeStyle(\n      recipeTw.classesToStyle(\n        cn(progressTrackClass(part.size), className),\n        part.theme,\n      ).style,\n      style,\n    )}\n  >\n    {children}\n  </div>\n);\n\nexport const MeterTrack = (props: MeterTrackProps): ReactElement =>\n  renderMeterTrack(props, useMeterPart(\"MeterTrack\"));\n\nexport interface MeterIndicatorProps {\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderMeterIndicator = (\n  { className, style, testId }: MeterIndicatorProps,\n  part: MeterPart,\n): ReactElement => (\n  <div\n    testId={testId}\n    style={mergeStyle(\n      mergeStyle(\n        recipeTw.classesToStyle(progressFillClass(part.tone), part.theme).style,\n        { width: `${part.fraction * 100}%` },\n      ),\n      className === undefined\n        ? style\n        : mergeStyle(\n            recipeTw.classesToStyle(className, part.theme).style,\n            style,\n          ),\n    )}\n  />\n);\n\nexport const MeterIndicator = (props: MeterIndicatorProps): ReactElement =>\n  renderMeterIndicator(props, useMeterPart(\"MeterIndicator\"));\n\nexport interface MeterLabelProps {\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const renderMeterLabel = (\n  { children, testId }: MeterLabelProps,\n  part: MeterPart,\n): ReactElement | null => {\n  const copy = captionCopy(children, part.label);\n  return copy === undefined ? null : (\n    <Text role=\"caption\" tone=\"muted\" testId={testId}>\n      {copy}\n    </Text>\n  );\n};\n\nexport const MeterLabel = (props: MeterLabelProps): ReactElement | null =>\n  renderMeterLabel(props, useMeterPart(\"MeterLabel\"));\n\nexport interface MeterValueProps {\n  readonly testId?: string;\n}\n\nexport const renderMeterValue = (\n  { testId }: MeterValueProps,\n  part: MeterPart,\n): ReactElement => (\n  <Text role=\"caption\" tone=\"muted\" testId={testId}>\n    {part.valueText}\n  </Text>\n);\n\nexport const MeterValue = (props: MeterValueProps): ReactElement =>\n  renderMeterValue(props, useMeterPart(\"MeterValue\"));\n\nexport const renderMeter = (\n  props: MeterProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const part = meterPart(props, theme);\n  return (\n    <div\n      ref={props.ref}\n      role=\"meter\"\n      aria-label={accessibleName(props.label)}\n      aria-id={props[\"aria-id\"]}\n      aria-valuetext={part.valueText}\n      testId={props.testId}\n      onLayout={props.onLayout}\n      style={mergeStyle(\n        mergeStyle(\n          frameStyle(theme),\n          recipeTw.classesToStyle(props.className, theme).style,\n        ),\n        props.style,\n      )}\n    >\n      <MeterPartContext.Provider value={part}>\n        {props.children ??\n          renderMeterTrack({ children: renderMeterIndicator({}, part) }, part)}\n      </MeterPartContext.Provider>\n    </div>\n  );\n};\n\nexport const Meter = (props: MeterProps): ReactElement =>\n  renderMeter(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/NumberField.tsx",
      "target": "@ui/gpuixui/primitives/components/NumberField.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  useRef,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type {\n  EventPayload,\n  KeyEvent,\n  PublicInstance,\n  StyleDesc,\n} from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { useGpuix } from \"@gpuix/react\";\nimport * as Option from \"effect/Option\";\nimport { cn } from \"../../tw/index.js\";\nimport { Icon } from \"../../icons/index.js\";\nimport { isPlainKey } from \"../core/keyboard.js\";\nimport { primaryButton } from \"../core/pointer.js\";\nimport {\n  clampNumeric,\n  commitNumberDraft,\n  decideNumberKey,\n  initNumberDraft,\n  normalizeNumberRange,\n  numberDisplay,\n  numberDraftText,\n  parseNumberText,\n  scrubNumber,\n  SCRUB_PIXELS_PER_STEP,\n  SHIFT_SCRUB_SCALE,\n  stepNumber,\n  syncNumberDraft,\n  withNumberDraftText,\n  withNumberDraftValue,\n  type NumberRange,\n  type ParseValue,\n} from \"../core/number.js\";\nimport { accessibleName, fieldHelp } from \"../core/a11y.js\";\nimport { controlRing, keepContent } from \"../core/focus.js\";\nimport { nativeTextTheme } from \"../core/style.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { colorRole, controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport {\n  numberFieldClass,\n  numberInputClass,\n  numberStepperClass,\n  numberStepperColumnClass,\n} from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport type { FieldState } from \"./Input.js\";\nimport { fieldHint, renderFieldHint } from \"./hint.js\";\nimport {\n  composeEvent,\n  mergeRefs,\n  mergeStyle,\n  suppressFeedback,\n  useControlState,\n  useFocus,\n  valueChange,\n  type FieldHostProps,\n  type FocusState,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface NumberFieldProps\n  extends\n    ValueChangeProps<number>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly value?: number;\n  readonly defaultValue?: number;\n\n  readonly min?: number;\n\n  readonly max?: number;\n\n  readonly step?: number;\n\n  readonly precision?: number;\n\n  readonly size?: ControlSize;\n  readonly disabled?: boolean;\n  readonly invalid?: boolean;\n\n  readonly label?: string;\n  readonly placeholder?: string;\n  readonly autoFocus?: boolean;\n\n  readonly onSubmit?: (event: EventPayload) => void;\n\n  readonly onValueCommit?: (value: number) => void;\n\n  readonly formatValue?: (value: number) => string;\n\n  readonly parseValue?: ParseValue;\n\n  readonly style?: StateStyle<FieldState>;\n\n  readonly className?: string;\n\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const numberRange = (props: {\n  readonly min?: number;\n  readonly max?: number;\n  readonly step?: number;\n}): NumberRange =>\n  normalizeNumberRange({\n    min: props.min,\n    max: props.max,\n    step: props.step ?? 1,\n  });\n\nexport interface NumberFieldHandlers {\n  readonly onTextChange: (text: string) => void;\n  readonly onCommit: () => void;\n  readonly onValue: (next: number) => void;\n  readonly registerInput: (instance: PublicInstance | null) => void;\n}\n\nexport interface NumberFieldScrub {\n  readonly onMouseDown: (event: EventPayload) => void;\n  readonly onMouseMove: (event: EventPayload) => void;\n  readonly onMouseUp: () => void;\n\n  readonly consumedClick: () => boolean;\n}\n\ninterface ScrubOptions {\n  readonly current: number;\n  readonly range: NumberRange;\n  readonly disabled: boolean;\n\n  readonly axis: \"x\" | \"y\";\n  readonly onValue: (next: number) => void;\n\n  readonly onEnd: (value: number) => void;\n}\n\nconst useNumberScrub = ({\n  current,\n  range,\n  disabled,\n  axis,\n  onValue,\n  onEnd,\n}: ScrubOptions): NumberFieldScrub => {\n  const at = (event: EventPayload): Option.Option<number> =>\n    Option.fromNullishOr(axis === \"y\" ? event.y : event.x);\n\n  const travel = (origin: number, now: number): number =>\n    axis === \"y\" ? origin - now : now - origin;\n  const press = useRef<\n    Option.Option<{ readonly at: number; readonly value: number }>\n  >(Option.none());\n  const scrubbed = useRef(false);\n\n  const last = useRef<Option.Option<number>>(Option.none());\n  return {\n    onMouseDown: (event) => {\n      if (!primaryButton(event)) return;\n\n      scrubbed.current = false;\n      last.current = Option.some(current);\n      press.current = Option.map(at(event), (at) => ({\n        at,\n        value: current,\n      }));\n    },\n    onMouseMove: (event) => {\n      if (disabled || event.pressedButton !== 0) {\n        press.current = Option.none();\n        return;\n      }\n      const held = event.modifiers?.shift === true ? SHIFT_SCRUB_SCALE : 1;\n      Option.match(\n        Option.all({\n          origin: press.current,\n          at: at(event),\n        }),\n        {\n          onNone: () => {},\n          onSome: ({ origin, at }) => {\n            const next = scrubNumber(\n              origin.value,\n              travel(origin.at, at),\n              range,\n              SCRUB_PIXELS_PER_STEP,\n              held,\n            );\n            if (next === current) return;\n            scrubbed.current = true;\n            last.current = Option.some(next);\n            onValue(next);\n          },\n        },\n      );\n    },\n    onMouseUp: () => {\n      const moved = scrubbed.current;\n      const ended = last.current;\n      press.current = Option.none();\n      last.current = Option.none();\n      if (!moved) return;\n\n      Option.match(ended, {\n        onNone: () => {},\n        onSome: onEnd,\n      });\n    },\n    consumedClick: () => {\n      const moved = scrubbed.current;\n      scrubbed.current = false;\n      return moved;\n    },\n  };\n};\n\nexport interface NumberFieldPart {\n  readonly theme: PrimitiveTheme;\n  readonly size: ControlSize;\n  readonly invalid: boolean;\n  readonly disabled: boolean;\n  readonly text: string;\n  readonly placeholder: string | undefined;\n  readonly label: string | undefined;\n  readonly autoFocus: boolean | undefined;\n\n  readonly address: FieldHostProps;\n\n  readonly testId: string | undefined;\n\n  readonly description: string | undefined;\n\n  readonly onSubmit: ((event: EventPayload) => void) | undefined;\n\n  readonly current: number;\n  readonly range: NumberRange;\n  readonly focus: FocusState;\n  readonly handlers: NumberFieldHandlers;\n\n  readonly scrub: Option.Option<NumberFieldScrub>;\n\n  readonly interaction: InteractionProps;\n  readonly atMin: boolean;\n  readonly atMax: boolean;\n}\n\nexport const numberFieldPart = (\n  props: NumberFieldProps,\n  theme: PrimitiveTheme,\n  state: ValueState<number>,\n  text: string,\n  focus: FocusState,\n  handlers: NumberFieldHandlers,\n  scrub: Option.Option<NumberFieldScrub> = Option.none(),\n): NumberFieldPart => {\n  const disabled = props.disabled === true;\n  const range = numberRange(props);\n\n  const current = clampNumeric(state.current, range);\n  return {\n    theme,\n    size: controlSize(props.size),\n    invalid: props.invalid === true,\n    disabled,\n    text,\n    placeholder: props.placeholder,\n    label: accessibleName(props.label),\n    autoFocus: disabled ? undefined : props.autoFocus,\n    address: props,\n    testId: props.testId,\n\n    description: fieldHelp(props, text),\n    onSubmit: disabled ? undefined : props.onSubmit,\n    current,\n    range,\n    focus,\n    handlers,\n    scrub,\n    interaction: props,\n    atMin: range.min !== undefined && current <= range.min,\n    atMax: range.max !== undefined && current >= range.max,\n  };\n};\n\nconst NumberFieldPartContext = createContext<NumberFieldPart | null>(null);\n\nconst useNumberFieldPart = (name: string): NumberFieldPart => {\n  const part = useContext(NumberFieldPartContext);\n  if (part === null) {\n    throw new Error(`${name} must be used inside NumberField`);\n  }\n  return part;\n};\n\nexport interface NumberFieldInputProps {\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nconst NUMBER_INPUT_FRAME: StyleDesc = {\n  position: \"relative\",\n  flexGrow: 1,\n  flexShrink: 1,\n  minWidth: 0,\n  height: \"100%\",\n};\n\nexport const renderNumberFieldInput = (\n  { className, style, testId }: NumberFieldInputProps,\n  part: NumberFieldPart,\n): ReactElement => {\n  const { disabled, focus, handlers } = part;\n\n  const chrome = mergeStyle(\n    mergeStyle(\n      mergeStyle(\n        recipeTw.classesToStyle(numberInputClass, part.theme).style,\n        recipeTw.classesToStyle(className, part.theme).style,\n      ),\n      keepContent,\n    ),\n    controlRing(part.theme, {\n      disabled: part.disabled,\n      invalid: part.invalid,\n    }),\n  );\n  const addressed = testId ?? part.testId;\n  const hint = fieldHint(part, part.theme, part.text, {\n    size: part.size,\n    align: \"center\",\n    origin: \"content\",\n  });\n  return (\n    <div style={NUMBER_INPUT_FRAME}>\n      <input\n        ref={mergeRefs(handlers.registerInput, part.address.ref)}\n        testId={testId}\n        value={part.text}\n        readOnly={disabled}\n        autoFocus={part.autoFocus}\n        tabIndex={disabled ? -1 : undefined}\n        theme={nativeTextTheme(part.theme)}\n        aria-label={part.label}\n        aria-description={part.description}\n        aria-id={part.address[\"aria-id\"]}\n        onLayout={part.address.onLayout}\n        style={mergeStyle({ width: \"100%\" }, mergeStyle(chrome, style))}\n        onChange={(event: EventPayload) => {\n          if (disabled) return;\n          handlers.onTextChange(event.value ?? \"\");\n        }}\n        onSubmit={\n          part.disabled\n            ? undefined\n            : (event: EventPayload) => {\n                handlers.onCommit();\n                part.onSubmit?.(event);\n              }\n        }\n        onClick={part.interaction.onClick}\n        onKeyDown={composeEvent<KeyEvent>(\n          part.interaction.onKeyDown,\n          (event) => {\n            if (disabled || !isPlainKey(event.modifiers)) return;\n            Option.match(\n              decideNumberKey(part.current, part.range, event.key ?? \"\"),\n              {\n                onNone: () => {},\n                onSome: handlers.onValue,\n              },\n            );\n          },\n        )}\n        onKeyUp={part.interaction.onKeyUp}\n        onFocus={composeEvent<EventPayload>(\n          part.interaction.onFocus,\n          focus.onFocus,\n        )}\n        onBlur={composeEvent<EventPayload>(part.interaction.onBlur, () => {\n          focus.onBlur();\n          handlers.onCommit();\n        })}\n      />\n      {Option.match(hint, {\n        onNone: () => null,\n        onSome: (hint) => renderFieldHint(hint, addressed),\n      })}\n    </div>\n  );\n};\n\nexport const NumberFieldInput = (props: NumberFieldInputProps): ReactElement =>\n  renderNumberFieldInput(props, useNumberFieldPart(\"NumberFieldInput\"));\n\nexport interface NumberFieldScrubHandleProps {\n  readonly value: number;\n\n  readonly min?: number;\n\n  readonly max?: number;\n\n  readonly step?: number;\n  readonly disabled?: boolean;\n\n  readonly onValueChange: (next: number) => void;\n\n  readonly onValueCommit?: (value: number) => void;\n\n  readonly children?: ReactNode;\n\n  readonly style?: StyleDesc;\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const NumberFieldScrubHandle = ({\n  value,\n  min,\n  max,\n  step,\n  disabled,\n  onValueChange,\n  onValueCommit,\n  children,\n  style,\n  className,\n  testId,\n}: NumberFieldScrubHandleProps): ReactElement => {\n  const theme = useTheme();\n  const range = numberRange({ min, max, step });\n  const gesture = useNumberScrub({\n    current: clampNumeric(value, range),\n    range,\n    disabled: disabled === true,\n    axis: \"x\",\n    onValue: onValueChange,\n    onEnd: (next) => onValueCommit?.(next),\n  });\n  const inert = disabled === true;\n  const wash = inert ? undefined : colorRole(theme, \"hover\");\n  return (\n    <div\n      role=\"presentation\"\n      testId={testId}\n      onMouseDown={inert ? undefined : gesture.onMouseDown}\n      onMouseMove={inert ? undefined : gesture.onMouseMove}\n      onMouseUp={inert ? undefined : gesture.onMouseUp}\n      style={mergeStyle(\n        {\n          display: \"flex\",\n          flexDirection: \"row\",\n          alignItems: \"center\",\n          minWidth: 0,\n          cursor: inert ? \"default\" : \"col-resize\",\n\n          ...(wash === undefined\n            ? {}\n            : {\n                hover: {\n                  backgroundColor: wash,\n                  borderRadius: theme.radius.sm,\n                },\n              }),\n        },\n        mergeStyle(recipeTw.classesToStyle(className, theme).style, style),\n      )}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport interface NumberFieldStepperProps {\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nconst renderStepper = (\n  { children, className, style, testId }: NumberFieldStepperProps,\n  part: NumberFieldPart,\n  direction: 1 | -1,\n): ReactElement => {\n  const blocked = direction === 1 ? part.atMax : part.atMin;\n  const inert = part.disabled || blocked;\n\n  const scrub = part.disabled ? Option.none<NumberFieldScrub>() : part.scrub;\n  const consumedClick = (): boolean =>\n    Option.match(scrub, {\n      onNone: () => false,\n      onSome: (gesture) => gesture.consumedClick(),\n    });\n\n  const glyphColor = inert\n    ? colorRole(part.theme, \"placeholder\")\n    : colorRole(part.theme, \"mutedForeground\");\n  return (\n    <div\n      role=\"presentation\"\n      testId={testId}\n      onMouseDown={Option.getOrUndefined(\n        Option.map(scrub, (gesture) => gesture.onMouseDown),\n      )}\n      onMouseMove={Option.getOrUndefined(\n        Option.map(scrub, (gesture) => gesture.onMouseMove),\n      )}\n      onMouseUp={Option.getOrUndefined(\n        Option.map(scrub, (gesture) => gesture.onMouseUp),\n      )}\n      onClick={composeEvent<EventPayload>(part.interaction.onClick, () => {\n        if (consumedClick() || inert) return;\n        part.handlers.onValue(stepNumber(part.current, part.range, direction));\n      })}\n      style={mergeStyle(\n        mergeStyle(\n          recipeTw.classesToStyle(numberStepperClass(inert), part.theme).style,\n          recipeTw.classesToStyle(className, part.theme).style,\n        ),\n        style,\n      )}\n    >\n      {children ??\n        (glyphColor === undefined ? null : (\n          <Icon\n            name={direction === 1 ? \"chevron-up\" : \"chevron-down\"}\n            size=\"xs\"\n            color={glyphColor}\n          />\n        ))}\n    </div>\n  );\n};\n\nexport const NumberFieldIncrement = (\n  props: NumberFieldStepperProps,\n): ReactElement =>\n  renderStepper(props, useNumberFieldPart(\"NumberFieldIncrement\"), 1);\n\nexport const NumberFieldDecrement = (\n  props: NumberFieldStepperProps,\n): ReactElement =>\n  renderStepper(props, useNumberFieldPart(\"NumberFieldDecrement\"), -1);\n\nexport const renderNumberField = (\n  props: NumberFieldProps,\n  theme: PrimitiveTheme,\n  state: ValueState<number>,\n  text: string,\n  focus: FocusState,\n  handlers: NumberFieldHandlers,\n  scrub?: NumberFieldScrub,\n): ReactElement => {\n  const part = numberFieldPart(\n    props,\n    theme,\n    state,\n    text,\n    focus,\n    handlers,\n    Option.fromNullishOr(scrub),\n  );\n  const resolved = recipeTw.classesToStyle(\n    cn(\n      numberFieldClass({ size: part.size, invalid: part.invalid }),\n      props.className,\n    ),\n    theme,\n  );\n  let chrome = resolved.style;\n  if (part.invalid) chrome = mergeStyle(chrome, resolved.invalid);\n  if (part.disabled)\n    chrome = suppressFeedback(mergeStyle(chrome, resolved.disabled));\n  chrome = mergeStyle(\n    chrome,\n    resolveStyle(props.style, {\n      disabled: part.disabled,\n      focused: focus.focused,\n      invalid: part.invalid,\n    }),\n  );\n  return (\n    <div testId={props.testId} style={chrome}>\n      <NumberFieldPartContext.Provider value={part}>\n        {props.children ?? (\n          <>\n            {renderNumberFieldInput({}, part)}\n            <div\n              style={\n                recipeTw.classesToStyle(numberStepperColumnClass, theme).style\n              }\n            >\n              {renderStepper({}, part, 1)}\n              {renderStepper({}, part, -1)}\n            </div>\n          </>\n        )}\n      </NumberFieldPartContext.Provider>\n    </div>\n  );\n};\n\nexport const NumberField = (props: NumberFieldProps): ReactElement => {\n  const theme = useTheme();\n  const { renderer } = useGpuix();\n  const focus = useFocus();\n  const range = numberRange(props);\n  const inputId = useRef<number | null>(null);\n  const incoming = Option.fromNullishOr(props.value);\n  const [draft, setDraft] = useState(() =>\n    initNumberDraft(\n      incoming,\n      Option.isSome(Option.fromNullishOr(props.defaultValue)),\n    ),\n  );\n  const state = useControlState(\n    props.value,\n    clampNumeric(props.defaultValue ?? 0, range),\n    valueChange(props),\n  );\n  const display = (value: number): string =>\n    numberDisplay(value, props.precision, props.formatValue);\n\n  const read = (text: string): Option.Option<number> =>\n    parseNumberText(text, props.parseValue);\n\n  const folded = syncNumberDraft(draft, incoming, range, props.parseValue);\n  if (Option.isSome(folded)) setDraft(folded.value);\n  const effective = Option.getOrElse(folded, () => draft);\n  const text = numberDraftText(\n    effective,\n    clampNumeric(state.value.current, range),\n    display,\n  );\n  const focusInput = (): void =>\n    Option.match(Option.fromNullishOr(inputId.current), {\n      onNone: () => {},\n      onSome: (id) => renderer?.focusElement?.(id),\n    });\n\n  const dirty = useRef(false);\n\n  const apply = (next: number): number => {\n    const clamped = clampNumeric(next, range);\n    state.set(clamped);\n    setDraft((current) => withNumberDraftValue(commitNumberDraft(current)));\n    dirty.current = false;\n    return clamped;\n  };\n\n  const scrub = useNumberScrub({\n    current: clampNumeric(state.value.current, range),\n    range,\n    disabled: props.disabled === true,\n    axis: \"y\",\n    onValue: (next) => {\n      apply(next);\n    },\n    onEnd: (value) => {\n      focusInput();\n      props.onValueCommit?.(value);\n    },\n  });\n  return renderNumberField(\n    props,\n    theme,\n    state.value,\n    text,\n    focus,\n    {\n      registerInput: (instance) => {\n        inputId.current = instance?.id ?? null;\n      },\n      onTextChange: (next) => {\n        const parsed = read(next);\n\n        dirty.current = Option.isSome(parsed);\n        setDraft((current) => {\n          const typed = withNumberDraftText(current, next);\n          return Option.isSome(parsed) ? withNumberDraftValue(typed) : typed;\n        });\n        Option.match(parsed, {\n          onNone: () => {},\n          onSome: (parsed) => state.set(clampNumeric(parsed, range)),\n        });\n      },\n      onCommit: () => {\n        const parsed = read(text);\n        const edited = dirty.current;\n        dirty.current = false;\n        Option.match(parsed, {\n          onNone: () => setDraft((current) => commitNumberDraft(current)),\n          onSome: (parsed) => {\n            const next = clampNumeric(parsed, range);\n            state.set(next);\n            setDraft((current) =>\n              withNumberDraftValue(commitNumberDraft(current)),\n            );\n            if (edited) props.onValueCommit?.(next);\n          },\n        });\n      },\n      onValue: (next) => {\n        const before = clampNumeric(state.value.current, range);\n        const value = apply(next);\n        focusInput();\n        if (value !== before) props.onValueCommit?.(value);\n      },\n    },\n    scrub,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/OTPField.tsx",
      "target": "@ui/gpuixui/primitives/components/OTPField.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  useRef,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport type {\n  EventPayload,\n  KeyEvent,\n  PublicInstance,\n  StyleDesc,\n} from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport { cn } from \"../../tw/index.js\";\nimport { isPlainKey } from \"../core/keyboard.js\";\nimport {\n  applyOtpInput,\n  decideOtpKey,\n  normalizeOtp,\n  otpCellCount,\n  otpCells,\n  otpEmptiedCell,\n  type OtpEdit,\n} from \"../core/otp.js\";\nimport { accessibleName, spokenSentence } from \"../core/a11y.js\";\nimport { controlRing, keepContent } from \"../core/focus.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport { nativeTextTheme } from \"../core/style.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { fieldSquareClass, otpGroupClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport type { FieldState } from \"./Input.js\";\nimport {\n  composeEvent,\n  mergeRefs,\n  mergeStyle,\n  suppressFeedback,\n  useControlState,\n  valueChange,\n  type FieldHostProps,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface OTPFieldProps\n  extends\n    ValueChangeProps<string>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly length?: number;\n\n  readonly value?: string;\n  readonly defaultValue?: string;\n\n  readonly onComplete?: (value: string) => void;\n\n  readonly onSubmit?: (event: EventPayload) => void;\n\n  readonly size?: ControlSize;\n\n  readonly disabled?: boolean;\n\n  readonly invalid?: boolean;\n\n  readonly label?: string;\n\n  readonly autoFocus?: boolean;\n\n  readonly style?: StateStyle<FieldState>;\n\n  readonly className?: string;\n\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport interface OTPFieldFocus {\n  readonly focusedIndex: Option.Option<number>;\n  readonly onFocus: (index: number) => void;\n\n  readonly onBlur: (index: number) => void;\n}\n\nexport interface OTPFieldHandlers {\n  readonly onEdit: (index: number, text: string) => void;\n  readonly onKey: (index: number, key: string) => void;\n\n  readonly onPick: (index: number) => void;\n  readonly registerCell: (\n    index: number,\n  ) => (instance: PublicInstance | null) => void;\n}\n\nexport const otpCellLabel = (\n  label: string | undefined,\n  index: number,\n  messages: PrimitiveMessages = defaultMessages,\n): string => {\n  const name = accessibleName(label);\n  return spokenSentence(\n    messages.codeDigit(index, name),\n    defaultMessages.codeDigit(index, name),\n  );\n};\n\nexport interface OTPFieldPart {\n  readonly theme: PrimitiveTheme;\n  readonly disabled: boolean;\n  readonly label: string | undefined;\n\n  readonly description: string | undefined;\n\n  readonly address: FieldHostProps;\n  readonly testId: string | undefined;\n  readonly autoFocus: boolean | undefined;\n\n  readonly cells: ReadonlyArray<string>;\n\n  readonly focused: boolean;\n\n  readonly rovingIndex: number;\n\n  readonly onSubmit: ((event: EventPayload) => void) | undefined;\n  readonly cellStyle: StyleDesc;\n  readonly focus: OTPFieldFocus;\n  readonly handlers: OTPFieldHandlers;\n\n  readonly interaction: InteractionProps;\n}\n\nexport const otpFieldPart = (\n  props: OTPFieldProps,\n  theme: PrimitiveTheme,\n  state: ValueState<string>,\n  focus: OTPFieldFocus,\n  handlers: OTPFieldHandlers,\n): OTPFieldPart => {\n  const limit = otpCellCount(props.length);\n  const disabled = props.disabled === true;\n  const resolved = recipeTw.classesToStyle(\n    fieldSquareClass({ size: controlSize(props.size), invalid: props.invalid }),\n    theme,\n  );\n  let cellStyle = mergeStyle(resolved.style, keepContent);\n  if (props.invalid === true)\n    cellStyle = mergeStyle(cellStyle, resolved.invalid);\n  if (disabled) {\n    cellStyle = suppressFeedback(mergeStyle(cellStyle, resolved.disabled));\n  } else {\n    cellStyle = mergeStyle(\n      cellStyle,\n      controlRing(theme, { disabled, invalid: props.invalid }),\n    );\n  }\n  const code = normalizeOtp(state.current, limit);\n\n  const focused = Option.filter(\n    focus.focusedIndex,\n    (index) => Number.isInteger(index) && index >= 0 && index < limit,\n  );\n  return {\n    theme,\n    disabled,\n    label: accessibleName(props.label),\n    description: accessibleName(props.description),\n    address: props,\n    testId: props.testId,\n    autoFocus: disabled ? undefined : props.autoFocus,\n    cells: otpCells(code, limit),\n    focused: Option.isSome(focused),\n    rovingIndex: Option.getOrElse(focused, () =>\n      Math.min(code.length, Math.max(0, limit - 1)),\n    ),\n    onSubmit: disabled ? undefined : props.onSubmit,\n    cellStyle,\n    focus,\n    handlers,\n    interaction: props,\n  };\n};\n\nconst OtpFieldPartContext = createContext<OTPFieldPart | null>(null);\n\nconst useOtpFieldPart = (name: string): OTPFieldPart => {\n  const part = useContext(OtpFieldPartContext);\n  if (part === null) {\n    throw new Error(`${name} must be used inside OTPField`);\n  }\n  return part;\n};\n\nexport interface OTPFieldInputProps {\n  readonly index: number;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderOtpFieldInput = (\n  { index, className, style, testId }: OTPFieldInputProps,\n  part: OTPFieldPart,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const { disabled, focus, handlers } = part;\n\n  const address: FieldHostProps = index === 0 ? part.address : {};\n  return (\n    <input\n      key={`cell-${index}`}\n      ref={mergeRefs(handlers.registerCell(index), address.ref)}\n      testId={\n        testId ??\n        (part.testId === undefined\n          ? undefined\n          : `${part.testId}-cell-${index + 1}`)\n      }\n      value={part.cells[index] ?? \"\"}\n      readOnly={disabled}\n      autoFocus={index === 0 ? part.autoFocus : undefined}\n      tabIndex={disabled ? -1 : index === part.rovingIndex ? 0 : -1}\n      theme={nativeTextTheme(part.theme)}\n      aria-label={otpCellLabel(part.label, index, messages)}\n      aria-description={part.description}\n      aria-id={address[\"aria-id\"]}\n      onLayout={address.onLayout}\n      style={mergeStyle(\n        mergeStyle(\n          part.cellStyle,\n          recipeTw.classesToStyle(className, part.theme).style,\n        ),\n        style,\n      )}\n      onChange={(event: EventPayload) => {\n        if (disabled) return;\n        handlers.onEdit(index, event.value ?? \"\");\n      }}\n      onSubmit={part.onSubmit}\n      onKeyUp={composeEvent<KeyEvent>(part.interaction.onKeyUp, (event) => {\n        if (disabled || !isPlainKey(event.modifiers)) return;\n        handlers.onKey(index, event.key ?? \"\");\n      })}\n      onClick={composeEvent<EventPayload>(part.interaction.onClick, () => {\n        if (disabled) return;\n        handlers.onPick(index);\n      })}\n      onKeyDown={part.interaction.onKeyDown}\n      onFocus={composeEvent<EventPayload>(part.interaction.onFocus, () =>\n        focus.onFocus(index),\n      )}\n      onBlur={composeEvent<EventPayload>(part.interaction.onBlur, () =>\n        focus.onBlur(index),\n      )}\n    />\n  );\n};\n\nexport const OTPFieldInput = (props: OTPFieldInputProps): ReactElement => {\n  const part = useOtpFieldPart(\"OTPFieldInput\");\n  return renderOtpFieldInput(props, part, useMessages());\n};\n\nexport const renderOtpField = (\n  props: OTPFieldProps,\n  theme: PrimitiveTheme,\n  state: ValueState<string>,\n  focus: OTPFieldFocus,\n  handlers: OTPFieldHandlers,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const part = otpFieldPart(props, theme, state, focus, handlers);\n  const disabled = part.disabled;\n  const group = recipeTw.classesToStyle(\n    cn(otpGroupClass, props.className),\n    theme,\n  ).style;\n  return (\n    <div\n      role=\"group\"\n      aria-label={accessibleName(props.label)}\n      testId={props.testId}\n      style={mergeStyle(\n        group,\n        resolveStyle(props.style, {\n          disabled,\n          focused: !disabled && part.focused,\n          invalid: props.invalid === true,\n        }),\n      )}\n    >\n      <OtpFieldPartContext.Provider value={part}>\n        {props.children ??\n          Arr.map(part.cells, (_cell, index) =>\n            renderOtpFieldInput({ index }, part, messages),\n          )}\n      </OtpFieldPartContext.Provider>\n    </div>\n  );\n};\n\nexport const OTPField = (props: OTPFieldProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const { renderer } = useGpuix();\n  const limit = otpCellCount(props.length);\n  const cellIds = useRef<Map<number, number>>(new Map());\n\n  const emptiedByEdit = useRef(false);\n  const [focusedIndex, setFocusedIndex] = useState<Option.Option<number>>(\n    Option.none(),\n  );\n  const state = useControlState(\n    props.value,\n    normalizeOtp(props.defaultValue, limit),\n    valueChange(props),\n  );\n  const focusCell = (index: number): void => {\n    setFocusedIndex(Option.some(index));\n    Option.match(\n      Option.all({\n        id: Option.fromNullishOr(cellIds.current.get(index)),\n        target: Option.fromNullishOr(renderer),\n      }),\n      {\n        onNone: () => {},\n        onSome: ({ id, target }) => target.focusElement?.(id),\n      },\n    );\n  };\n  const apply = (edit: OtpEdit): void => {\n    const previous = normalizeOtp(state.value.current, limit);\n    if (edit.value !== previous) {\n      state.set(edit.value);\n\n      if (previous.length < limit && edit.value.length === limit)\n        props.onComplete?.(edit.value);\n    }\n    focusCell(edit.focus);\n  };\n  const focus: OTPFieldFocus = {\n    focusedIndex,\n    onFocus: (index) => setFocusedIndex(Option.some(index)),\n    onBlur: (index) =>\n      setFocusedIndex((current) =>\n        Option.filter(current, (focused) => focused !== index),\n      ),\n  };\n  const handlers: OTPFieldHandlers = {\n    onEdit: (index, text) => {\n      const current = state.value.current;\n      const edit = applyOtpInput(current, index, text, limit);\n      emptiedByEdit.current = otpEmptiedCell(current, edit.value, index, limit);\n      apply(edit);\n    },\n    onKey: (index, key) => {\n      const alreadyEmptied = emptiedByEdit.current;\n      emptiedByEdit.current = false;\n      if (key === \"backspace\" && alreadyEmptied) return;\n      Option.match(decideOtpKey(state.value.current, index, key, limit), {\n        onNone: () => {},\n        onSome: apply,\n      });\n    },\n    onPick: (index) =>\n      focusCell(\n        Math.min(index, normalizeOtp(state.value.current, limit).length),\n      ),\n    registerCell: (index) => (instance) => {\n      if (instance === null) cellIds.current.delete(index);\n      else cellIds.current.set(index, instance.id);\n    },\n  };\n  return renderOtpField(props, theme, state.value, focus, handlers, messages);\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Progress.tsx",
      "target": "@ui/gpuixui/primitives/components/Progress.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport * as Option from \"effect/Option\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { cn } from \"../../tw/index.js\";\nimport {\n  accessibleName,\n  captionCopy,\n  spokenText,\n  visibleCopy,\n} from \"../core/a11y.js\";\nimport { storedNumber } from \"../core/stored.js\";\nimport {\n  defaultMessages,\n  indeterminateWord,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\nimport {\n  indeterminateOffset,\n  progressAnimates,\n  SPINNER_SEGMENTS,\n  STEP_INTERVAL_MS,\n} from \"../core/loading.js\";\nimport { reducedMotionOf, stillFrame } from \"../core/motion.js\";\nimport {\n  progressPercent,\n  progressSizeOf,\n  progressToneOf,\n  type ProgressSize,\n  type ProgressSpec,\n  type ProgressTone,\n} from \"../core/style.js\";\nimport { spaceValue } from \"../core/theme.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport {\n  progressBufferClass,\n  progressFillClass,\n  progressTrackClass,\n} from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, useStep, type ElementHostProps } from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { Text } from \"./Text.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface ProgressProps extends ElementHostProps {\n  readonly value?: number;\n\n  readonly buffer?: number;\n\n  readonly tone?: ProgressTone;\n\n  readonly size?: ProgressSpec[\"size\"];\n\n  readonly label?: string;\n\n  readonly formatValue?: (percent: number) => string;\n\n  readonly loadingText?: string;\n\n  readonly reducedMotion?: boolean;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nconst SEGMENT = 40;\n\nexport interface ProgressPart {\n  readonly theme: PrimitiveTheme;\n  readonly determinate: boolean;\n  readonly percent: number;\n\n  readonly bufferPercent: number | undefined;\n  readonly tone: ProgressTone;\n  readonly size: ProgressSize;\n  readonly step: number;\n  readonly label: string | undefined;\n\n  readonly valueText: string;\n}\n\nexport const progressPart = (\n  {\n    value,\n    buffer,\n    tone = \"primary\",\n    size = \"md\",\n    label,\n    formatValue,\n    loadingText,\n  }: ProgressProps,\n  theme: PrimitiveTheme,\n  step: number,\n  messages: PrimitiveMessages = defaultMessages,\n): ProgressPart => {\n  const measurement = storedNumber(value);\n  const determinate = Option.isSome(measurement);\n  const percent = Option.match(measurement, {\n    onNone: () => 0,\n    onSome: progressPercent,\n  });\n  return {\n    theme,\n    determinate,\n    percent,\n\n    bufferPercent: determinate\n      ? Option.match(storedNumber(buffer), {\n          onNone: () => undefined,\n          onSome: (held) => Math.max(percent, progressPercent(held)),\n        })\n      : undefined,\n    tone: progressToneOf(tone),\n    size: progressSizeOf(size),\n    step,\n\n    label: visibleCopy(label),\n\n    valueText: determinate\n      ? spokenText(Option.fromNullishOr(formatValue?.(percent)), `${percent}%`)\n      : spokenText(\n          Option.fromNullishOr(loadingText),\n\n          indeterminateWord(messages.loading),\n        ),\n  };\n};\n\nconst ProgressPartContext = createContext<ProgressPart | null>(null);\n\nconst useProgressPart = (name: string): ProgressPart => {\n  const part = useContext(ProgressPartContext);\n  if (part === null) {\n    throw new Error(`${name} must be used inside Progress`);\n  }\n  return part;\n};\n\nconst frameStyle = (theme: PrimitiveTheme): StyleDesc => ({\n  width: \"100%\",\n  display: \"flex\",\n  flexDirection: \"column\",\n  gap: spaceValue(theme, \"2xs\"),\n});\n\nexport interface ProgressTrackProps {\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderProgressTrack = (\n  { children, className, style, testId }: ProgressTrackProps,\n  part: ProgressPart,\n): ReactElement => (\n  <div\n    testId={testId}\n    style={mergeStyle(\n      recipeTw.classesToStyle(\n        cn(progressTrackClass(part.size), className),\n        part.theme,\n      ).style,\n      style,\n    )}\n  >\n    {children}\n  </div>\n);\n\nexport const ProgressTrack = (props: ProgressTrackProps): ReactElement =>\n  renderProgressTrack(props, useProgressPart(\"ProgressTrack\"));\n\nexport interface ProgressIndicatorProps {\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderProgressIndicator = (\n  { className, style, testId }: ProgressIndicatorProps,\n  part: ProgressPart,\n): ReactElement => {\n  const fill = mergeStyle(\n    mergeStyle(\n      recipeTw.classesToStyle(progressFillClass(part.tone), part.theme).style,\n      { width: `${part.determinate ? part.percent : SEGMENT}%` },\n    ),\n    className === undefined\n      ? style\n      : mergeStyle(recipeTw.classesToStyle(className, part.theme).style, style),\n  );\n  if (part.determinate) {\n    const held = part.bufferPercent;\n    return (\n      <>\n        <div testId={testId} style={fill} />\n        {held === undefined || held <= part.percent ? null : (\n          <div\n            style={mergeStyle(\n              recipeTw.classesToStyle(\n                progressBufferClass(part.tone),\n                part.theme,\n              ).style,\n              { width: `${held - part.percent}%` },\n            )}\n          />\n        )}\n      </>\n    );\n  }\n  const lead = mergeStyle(\n    recipeTw.classesToStyle(\"shrink-0\", part.theme).style,\n    { width: `${indeterminateOffset(part.step, SEGMENT)}%` },\n  );\n  return (\n    <>\n      <div style={lead} />\n      <div testId={testId} style={fill} />\n    </>\n  );\n};\n\nexport const ProgressIndicator = (\n  props: ProgressIndicatorProps,\n): ReactElement =>\n  renderProgressIndicator(props, useProgressPart(\"ProgressIndicator\"));\n\nexport interface ProgressLabelProps {\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const renderProgressLabel = (\n  { children, testId }: ProgressLabelProps,\n  part: ProgressPart,\n): ReactElement | null => {\n  const copy = captionCopy(children, part.label);\n  return copy === undefined ? null : (\n    <Text role=\"caption\" tone=\"muted\" testId={testId}>\n      {copy}\n    </Text>\n  );\n};\n\nexport const ProgressLabel = (props: ProgressLabelProps): ReactElement | null =>\n  renderProgressLabel(props, useProgressPart(\"ProgressLabel\"));\n\nexport interface ProgressValueProps {\n  readonly testId?: string;\n}\n\nexport const renderProgressValue = (\n  { testId }: ProgressValueProps,\n  part: ProgressPart,\n): ReactElement => (\n  <Text role=\"caption\" tone=\"muted\" testId={testId}>\n    {part.valueText}\n  </Text>\n);\n\nexport const ProgressValue = (props: ProgressValueProps): ReactElement =>\n  renderProgressValue(props, useProgressPart(\"ProgressValue\"));\n\nexport const renderProgress = (\n  props: ProgressProps,\n  theme: PrimitiveTheme,\n  step: number,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const {\n    label,\n    style,\n    className,\n    children,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n  } = props;\n  const part = progressPart(\n    props,\n    theme,\n    stillFrame(step, props.reducedMotion),\n    messages,\n  );\n  return (\n    <div\n      ref={ref}\n      role=\"progressbar\"\n      aria-label={accessibleName(label)}\n      aria-id={controlId}\n      aria-valuetext={part.valueText}\n      testId={testId}\n      onLayout={onLayout}\n      style={mergeStyle(\n        mergeStyle(\n          frameStyle(theme),\n          recipeTw.classesToStyle(className, theme).style,\n        ),\n        style,\n      )}\n    >\n      <ProgressPartContext.Provider value={part}>\n        {children ??\n          renderProgressTrack(\n            { children: renderProgressIndicator({}, part) },\n            part,\n          )}\n      </ProgressPartContext.Provider>\n    </div>\n  );\n};\n\nexport const Progress = (props: ProgressProps): ReactElement => {\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n\n  const animates =\n    progressAnimates(storedNumber(props.value)) && !reducedMotion;\n  return renderProgress(\n    { ...props, reducedMotion },\n    useTheme(),\n    useStep(SPINNER_SEGMENTS, STEP_INTERVAL_MS, animates),\n    useMessages(),\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/RadioGroup.tsx",
      "target": "@ui/gpuixui/primitives/components/RadioGroup.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport type { EventPayload, KeyEvent } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport {\n  decideAbsolute,\n  decideActivation,\n  decideMove,\n  isPlainKey,\n  orientationOf,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport { rovingIndex } from \"../core/roving.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport type {\n  SelectionAccent,\n  SelectionSpec,\n  SelectionState,\n} from \"../core/selection.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport {\n  radioDotClass,\n  selectionBoxClass,\n  stackClass,\n} from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  focusedOption,\n  groupOptions,\n  optionBody,\n  optionDisabled,\n  optionHelp,\n  optionItems,\n  optionName,\n  optionRowStyle,\n  useRovingFocus,\n  useTypeAhead,\n  type GroupFocus,\n  type GroupHandlers,\n  type GroupItemProps,\n  type GroupKeys,\n  type GroupOption,\n} from \"./group.js\";\nimport {\n  composeEvent,\n  composePress,\n  decorative,\n  mergeRefs,\n  mergeStyle,\n  suppressSelectionFeedback,\n  useControlState,\n  valueChange,\n  type FieldHostProps,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { selectionValueText } from \"./Checkbox.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport type RadioOption = GroupOption;\n\nexport type RadioGroupItemProps = GroupItemProps;\n\nexport const RadioGroupItem = (_props: RadioGroupItemProps): null => null;\n\nexport const radioItems = (props: {\n  readonly options?: ReadonlyArray<RadioOption>;\n  readonly children?: ReactNode;\n}): ReadonlyArray<RadioOption> =>\n  props.options === undefined || props.options === null\n    ? optionItems(props.children, RadioGroupItem)\n    : groupOptions(props.options);\n\nexport interface RadioGroupProps\n  extends\n    ValueChangeProps<string>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly options?: ReadonlyArray<RadioOption>;\n\n  readonly children?: ReactNode;\n\n  readonly value?: string;\n  readonly defaultValue?: string;\n\n  readonly orientation?: \"vertical\" | \"horizontal\";\n\n  readonly size?: ControlSize;\n\n  readonly accent?: SelectionAccent;\n\n  readonly disabled?: boolean;\n\n  readonly invalid?: boolean;\n\n  readonly label?: string;\n\n  readonly style?: StateStyle<RadioGroupState>;\n\n  readonly itemStyle?: StateStyle<RadioItemState>;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n\n  readonly itemTestId?: (value: string) => string;\n}\n\nexport interface RadioGroupState {\n  readonly disabled: boolean;\n\n  readonly focused: boolean;\n}\n\nexport interface RadioItemState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly checked: boolean;\n}\n\nexport type RadioGroupFocus = GroupFocus;\n\nexport interface RadioGroupHandlers extends GroupHandlers, GroupKeys {\n  readonly onSelect: (value: string) => void;\n}\n\nexport const initialActiveIndex = (\n  options: ReadonlyArray<RadioOption>,\n  value: string,\n): number => {\n  const indexOf = (matches: (option: RadioOption) => boolean): number =>\n    Option.getOrElse(Arr.findFirstIndex(options, matches), () => -1);\n  const selected = indexOf((option) => option.value === value);\n  if (selected >= 0) return selected;\n  return Math.max(\n    indexOf((option) => option.disabled !== true),\n    0,\n  );\n};\n\nconst itemState = (option: RadioOption, value: string): SelectionState =>\n  option.value === value ? \"checked\" : \"unchecked\";\n\nexport const renderRadioGroup = (\n  props: RadioGroupProps,\n  theme: PrimitiveTheme,\n  state: ValueState<string>,\n  activeIndex: number,\n  focus: RadioGroupFocus,\n  handlers: RadioGroupHandlers,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const orientation = orientationOf(props.orientation, \"vertical\");\n  const { accent = \"primary\" } = props;\n  const size = controlSize(props.size);\n  const invalid = props.invalid === true;\n  const options = radioItems(props);\n  const count = Arr.length(options);\n  const isDisabled = optionDisabled(options, props.disabled);\n\n  const focused = focusedOption(count, focus.focusedIndex);\n\n  const from = Option.getOrElse(\n    Option.filter(focused, (index) => !isDisabled(index)),\n    () => activeIndex,\n  );\n\n  const stopIndex = Option.getOrElse(\n    rovingIndex(count, Option.some(from), isDisabled),\n    () => from,\n  );\n  const select = (index: number): boolean => {\n    const option = options[index];\n    if (\n      props.disabled === true ||\n      option === undefined ||\n      option.disabled === true\n    )\n      return false;\n    handlers.onSelect(option.value);\n    return true;\n  };\n\n  const navigate = (index: number): boolean => {\n    if (!select(index)) return false;\n    handlers.onFocusMove(index);\n    return true;\n  };\n  const move = (key: string): boolean =>\n    Option.match(decideMove(stopIndex, count, key, orientation, isDisabled), {\n      onNone: () => false,\n      onSome: navigate,\n    });\n  const jump = (key: string): boolean =>\n    Option.match(decideAbsolute(count, key, isDisabled), {\n      onNone: () => false,\n      onSome: navigate,\n    });\n  const onKeyDown = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    const key = event.key ?? \"\";\n    if (swallowKeyDefault(key, \"down\")) event.preventDefault();\n    if (move(key) || jump(key)) return;\n\n    const typed = Option.flatMap(\n      Option.fromNullishOr(handlers.typeAhead),\n      (match) => match(stopIndex, key),\n    );\n    if (Option.match(typed, { onNone: () => false, onSome: navigate })) return;\n    if (decideActivation(key, \"down\", event.isHeld === true)) select(stopIndex);\n  };\n\n  const onKeyUp = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    if (decideActivation(event.key ?? \"\", \"up\", false)) select(stopIndex);\n  };\n  const group = mergeStyle(\n    recipeTw.classesToStyle(\n      cn(\n        stackClass({\n          direction: orientation === \"horizontal\" ? \"row\" : \"column\",\n          gap: \"sm\",\n        }),\n        props.className,\n      ),\n      theme,\n    ).style,\n    resolveStyle(props.style, {\n      disabled: props.disabled === true,\n      focused: Option.isSome(focused),\n    }),\n  );\n  return (\n    <div\n      role=\"radiogroup\"\n      aria-label={accessibleName(props.label)}\n      aria-description={accessibleName(props.description)}\n      testId={props.testId}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, onKeyDown)}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, onKeyUp)}\n      style={group}\n    >\n      {Arr.map(options, (option, index) => {\n        const current = itemState(option, state.current);\n        const disabled = props.disabled === true || option.disabled === true;\n\n        const addressed = index === stopIndex;\n        const spec: SelectionSpec = {\n          state: current,\n          size,\n          accent,\n          shape: \"circle\",\n          disabled,\n          invalid,\n        };\n        const resolved = recipeTw.classesToStyle(\n          selectionBoxClass(spec),\n          theme,\n        );\n        const box = decorative(\n          disabled\n            ? suppressSelectionFeedback(\n                mergeStyle(resolved.style, resolved.disabled),\n              )\n            : resolved.style,\n        );\n        const row = mergeStyle(\n          optionRowStyle({ disabled, invalid }, theme),\n          resolveStyle(props.itemStyle, {\n            disabled,\n            focused: Option.contains(focused, index),\n            checked: current === \"checked\",\n          }),\n        );\n        return (\n          <div\n            key={option.value}\n            ref={\n              addressed\n                ? mergeRefs(handlers.registerItem(index), props.ref)\n                : handlers.registerItem(index)\n            }\n            role=\"radio\"\n            aria-label={optionName(option)}\n            aria-description={optionHelp(option)}\n            aria-id={addressed ? props[\"aria-id\"] : undefined}\n            aria-valuetext={selectionValueText(current, messages.selection)}\n            testId={props.itemTestId?.(option.value)}\n            tabIndex={disabled ? undefined : index === stopIndex ? 0 : -1}\n            onLayout={addressed ? props.onLayout : undefined}\n            onClick={composePress(disabled, props.onClick, () => {\n              select(index);\n            })}\n            onFocus={composeEvent<EventPayload>(props.onFocus, () =>\n              focus.onFocus(index),\n            )}\n            onBlur={composeEvent<EventPayload>(props.onBlur, () =>\n              focus.onBlur(index),\n            )}\n            style={row}\n          >\n            <div style={box}>\n              {current === \"checked\" ? (\n                <div\n                  style={\n                    recipeTw.classesToStyle(radioDotClass(spec), theme).style\n                  }\n                />\n              ) : null}\n            </div>\n            {}\n            {optionBody(option, disabled, theme)}\n          </div>\n        );\n      })}\n    </div>\n  );\n};\n\nexport const RadioGroup = (props: RadioGroupProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const focus = useRovingFocus();\n  const selected = useControlState(\n    props.value,\n    props.defaultValue ?? \"\",\n    valueChange(props),\n  );\n  const options = radioItems(props);\n  const typeAhead = useTypeAhead(\n    options,\n    optionDisabled(options, props.disabled),\n  );\n  return renderRadioGroup(\n    props,\n    theme,\n    selected.value,\n    initialActiveIndex(options, selected.value.current),\n    focus,\n    { onSelect: selected.set, ...focus, typeAhead },\n    messages,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Rating.tsx",
      "target": "@ui/gpuixui/primitives/components/Rating.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport { useRef, useState, type ReactElement } from \"react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport type { EventPayload, KeyEvent, PublicInstance } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport { cn } from \"../../tw/index.js\";\nimport { Icon } from \"../../icons/index.js\";\nimport { accessibleName, spokenSentence } from \"../core/a11y.js\";\nimport {\n  decideActivation,\n  isPlainKey,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport {\n  defaultMessages,\n  starWord,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\nimport {\n  clampRating,\n  decideRating,\n  pointerRating,\n  ratingTabStop,\n  starCount,\n  starFill,\n  starState,\n  type StarBox,\n} from \"../core/rating.js\";\nimport {\n  ratingPartialCellStyle,\n  ratingPartialStyle,\n  ratingStarColor,\n} from \"../core/style.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { ratingGroupClass, ratingStarClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport {\n  composeEvent,\n  composePress,\n  mergeRefs,\n  mergeStyle,\n  useControlState,\n  valueChange,\n  type FieldHostProps,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\n\nexport interface RatingProps\n  extends\n    ValueChangeProps<number>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly value?: number;\n  readonly defaultValue?: number;\n\n  readonly max?: number;\n\n  readonly allowHalf?: boolean;\n\n  readonly size?: ControlSize;\n  readonly disabled?: boolean;\n\n  readonly readOnly?: boolean;\n\n  readonly label?: string;\n\n  readonly invalid?: boolean;\n\n  readonly style?: StateStyle<RatingState>;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport interface RatingState {\n  readonly disabled: boolean;\n  readonly readOnly: boolean;\n\n  readonly focused: boolean;\n  readonly invalid: boolean;\n}\n\nexport interface RatingFocus {\n  readonly focusedIndex: Option.Option<number>;\n  readonly onFocus: (index: number) => void;\n  readonly onBlur: (index: number) => void;\n}\n\nexport interface RatingHover {\n  readonly hovered: Option.Option<number>;\n  readonly onHover: (score: number) => void;\n\n  readonly onHoverEnd: () => void;\n}\n\nexport interface RatingHandlers {\n  readonly onSelect: (value: number) => void;\n\n  readonly onMove: (value: number) => void;\n  readonly registerItem: (\n    index: number,\n  ) => (instance: PublicInstance | null) => void;\n\n  readonly scoreAt?: (index: number, event: EventPayload) => number;\n}\n\nexport const ratingMax = (max: unknown): number =>\n  Option.match(Option.fromNullishOr(max), {\n    onNone: () => 5,\n    onSome: (count) => starCount(count),\n  });\n\nconst NO_POINTER: RatingHover = {\n  hovered: Option.none(),\n  onHover: () => {},\n  onHoverEnd: () => {},\n};\n\nexport const renderRating = (\n  props: RatingProps,\n  theme: PrimitiveTheme,\n  state: ValueState<number>,\n  focus: RatingFocus,\n  handlers: RatingHandlers,\n  hover: RatingHover = NO_POINTER,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const max = ratingMax(props.max);\n  const half = props.allowHalf === true;\n  const value = clampRating(state.current, max, half);\n  const tabStop = ratingTabStop(value, max, half);\n\n  const starName = (index: number): string =>\n    spokenSentence(\n      messages.starName(index, max),\n      defaultMessages.starName(index, max),\n    );\n\n  const focusedIndex = Option.filter(\n    focus.focusedIndex,\n    (index) => index >= 1 && index <= max,\n  );\n  const interactive = props.disabled !== true && props.readOnly !== true;\n\n  const lit = interactive\n    ? clampRating(\n        Option.getOrElse(hover.hovered, () => value),\n        max,\n        half,\n      )\n    : value;\n\n  const focusedStar = clampRating(\n    Option.getOrElse(focusedIndex, () => tabStop),\n    max,\n  );\n  const selectFocused = (): void => {\n    handlers.onSelect(focusedStar);\n  };\n  const scoreAt = handlers.scoreAt ?? ((index: number) => index);\n  const resolved = recipeTw.classesToStyle(\n    ratingStarClass({ disabled: props.disabled, readOnly: props.readOnly }),\n    theme,\n  );\n  let starStyle = resolved.style;\n  if (props.disabled === true) {\n    starStyle = mergeStyle(starStyle, resolved.disabled);\n  } else if (interactive) {\n    starStyle = mergeStyle(\n      starStyle,\n      controlRing(theme, { invalid: props.invalid }),\n    );\n  }\n\n  const glyph = (filled: boolean): ReactElement | null => {\n    const color = ratingStarColor(filled, theme, props.invalid);\n    return color === undefined ? null : (\n      <Icon\n        name={filled ? \"star-filled\" : \"star\"}\n        size={controlSize(props.size)}\n        color={color}\n      />\n    );\n  };\n  const star = (index: number): ReactElement => {\n    const fill = starFill(index, lit);\n    return (\n      <div\n        key={index}\n        ref={\n          index === tabStop\n            ? mergeRefs(handlers.registerItem(index), props.ref)\n            : handlers.registerItem(index)\n        }\n        role=\"radio\"\n        aria-label={starName(index)}\n        aria-id={index === tabStop ? props[\"aria-id\"] : undefined}\n        aria-valuetext={starWord(starState(index, value), messages.star)}\n        tabIndex={interactive ? (index === tabStop ? 0 : -1) : undefined}\n        onLayout={index === tabStop ? props.onLayout : undefined}\n        onClick={composePress(!interactive, props.onClick, (event) => {\n          handlers.onSelect(scoreAt(index, event));\n        })}\n        onMouseEnter={interactive ? () => hover.onHover(index) : undefined}\n        onMouseMove={\n          interactive && half\n            ? (event: EventPayload) => hover.onHover(scoreAt(index, event))\n            : undefined\n        }\n        onFocus={composeEvent<EventPayload>(props.onFocus, () =>\n          focus.onFocus(index),\n        )}\n        onBlur={composeEvent<EventPayload>(props.onBlur, () =>\n          focus.onBlur(index),\n        )}\n        style={starStyle}\n      >\n        {fill >= 1 ? (\n          glyph(true)\n        ) : (\n          <>\n            {glyph(false)}\n            {fill > 0 ? (\n              <div style={ratingPartialStyle(fill)}>\n                <div style={ratingPartialCellStyle(theme)}>{glyph(true)}</div>\n              </div>\n            ) : null}\n          </>\n        )}\n      </div>\n    );\n  };\n  return (\n    <div\n      role=\"radiogroup\"\n      aria-label={accessibleName(props.label)}\n      aria-description={accessibleName(props.description)}\n      testId={props.testId}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, (event) => {\n        if (!interactive || !isPlainKey(event.modifiers)) return;\n        const key = event.key ?? \"\";\n        if (swallowKeyDefault(key, \"down\")) event.preventDefault();\n        Option.match(decideRating(value, max, key, half), {\n          onNone: () => {},\n          onSome: handlers.onMove,\n        });\n        if (decideActivation(key, \"down\", event.isHeld === true))\n          selectFocused();\n      })}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, (event) => {\n        if (!interactive || !isPlainKey(event.modifiers)) return;\n        if (decideActivation(event.key ?? \"\", \"up\", false)) selectFocused();\n      })}\n      onMouseLeave={interactive ? hover.onHoverEnd : undefined}\n      style={mergeStyle(\n        recipeTw.classesToStyle(cn(ratingGroupClass, props.className), theme)\n          .style,\n        resolveStyle(props.style, {\n          disabled: props.disabled === true,\n          readOnly: props.readOnly === true,\n          focused: Option.isSome(focusedIndex),\n          invalid: props.invalid === true,\n        }),\n      )}\n    >\n      {Arr.map(Arr.range(1, max), star)}\n    </div>\n  );\n};\n\nexport const Rating = (props: RatingProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const { renderer } = useGpuix();\n  const itemIds = useRef<Map<number, number>>(new Map());\n  const half = props.allowHalf === true;\n  const max = ratingMax(props.max);\n  const [focusedIndex, setFocusedIndex] = useState<Option.Option<number>>(\n    Option.none(),\n  );\n\n  const [hovered, setHovered] = useState<Option.Option<number>>(Option.none());\n  const state = useControlState(\n    props.value,\n    clampRating(props.defaultValue ?? 0, max, half),\n    valueChange(props),\n  );\n\n  const boxOf = (index: number): Option.Option<StarBox> =>\n    Option.flatMap(Option.fromNullishOr(itemIds.current.get(index)), (id) =>\n      Option.fromNullishOr(renderer?.getElementBounds?.(id)),\n    );\n  const scoreAt = (index: number, event: EventPayload): number =>\n    pointerRating(index, Option.fromNullishOr(event.x), boxOf(index), half);\n  return renderRating(\n    props,\n    theme,\n    state.value,\n    {\n      focusedIndex,\n      onFocus: (index) => setFocusedIndex(Option.some(index)),\n      onBlur: (index) =>\n        setFocusedIndex((current) =>\n          Option.filter(current, (focused) => focused !== index),\n        ),\n    },\n    {\n      onSelect: state.set,\n      onMove: (value) => {\n        state.set(value);\n        const star = ratingTabStop(value, max, half);\n        Option.match(Option.fromNullishOr(itemIds.current.get(star)), {\n          onNone: () => {},\n          onSome: (id) => renderer?.focusElement?.(id),\n        });\n        setFocusedIndex(Option.some(star));\n      },\n      registerItem: (index) => (instance) => {\n        if (instance === null) itemIds.current.delete(index);\n        else itemIds.current.set(index, instance.id);\n      },\n      scoreAt,\n    },\n    {\n      hovered,\n      onHover: (score) =>\n        setHovered((current) =>\n          Option.contains(current, score) ? current : Option.some(score),\n        ),\n      onHoverEnd: () => setHovered(Option.none()),\n    },\n    messages,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/ScrollArea.tsx",
      "target": "@ui/gpuixui/primitives/components/ScrollArea.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  Children,\n  createContext,\n  Fragment,\n  isValidElement,\n  useContext,\n  useRef,\n  type ReactElement,\n  type ReactNode,\n  type Ref,\n} from \"react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport type {\n  EventPayload,\n  KeyEvent,\n  PublicInstance,\n  StyleDesc,\n} from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport { isPlainKey } from \"../core/keyboard.js\";\nimport { spokenWord } from \"../core/a11y.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport { scrollTarget } from \"../core/scroll.js\";\nimport { scrollAxis, type ScrollSpec } from \"../core/style.js\";\nimport { colorRole, rowHeightOf } from \"../core/theme.js\";\nimport type { PrimitiveTheme, RadiusStep, SpaceStep } from \"../core/theme.js\";\nimport { scrollClass, scrollFrameClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeRefs, mergeStyle } from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\ninterface ScrollPart {\n  readonly theme: PrimitiveTheme;\n  readonly axis: ScrollSpec[\"axis\"];\n  readonly onScroll: ((event: EventPayload) => void) | undefined;\n  readonly onScrollChange: ((event: EventPayload) => void) | undefined;\n  readonly onLayout: ((event: EventPayload) => void) | undefined;\n  readonly scrollbarColor: string | undefined;\n  readonly scrollbarWidth: number | undefined;\n\n  readonly focusable: boolean;\n\n  readonly label: string | undefined;\n\n  readonly onScrollKey: ((event: KeyEvent) => void) | undefined;\n}\n\nexport const DEFAULT_SCROLL_NAME = defaultMessages.scrollRegion;\n\nconst ScrollPartContext = createContext<ScrollPart | null>(null);\n\nconst useScrollPart = (name: string): ScrollPart => {\n  const part = useContext(ScrollPartContext);\n  if (part === null) {\n    throw new Error(`${name} must be used inside ScrollArea`);\n  }\n  return part;\n};\n\nexport const scrollPart = (\n  {\n    axis: requestedAxis,\n    onScroll,\n    onScrollChange,\n    onLayout,\n    scrollbarColor,\n    scrollbarWidth,\n    focusable,\n    label,\n  }: Pick<\n    ScrollAreaProps,\n    | \"axis\"\n    | \"onScroll\"\n    | \"onScrollChange\"\n    | \"onLayout\"\n    | \"scrollbarColor\"\n    | \"scrollbarWidth\"\n    | \"focusable\"\n    | \"label\"\n  >,\n  theme: PrimitiveTheme,\n  onScrollKey?: (event: KeyEvent) => void,\n): ScrollPart => ({\n  theme,\n  axis: scrollAxis(requestedAxis),\n  onScroll,\n  onScrollChange,\n  onLayout,\n  scrollbarColor: scrollbarColor ?? colorRole(theme, \"scrollbar\"),\n  scrollbarWidth,\n  focusable: focusable ?? true,\n  label,\n  onScrollKey,\n});\n\nexport interface ScrollAreaProps extends ScrollSpec {\n  readonly onScroll?: (event: EventPayload) => void;\n\n  readonly onScrollChange?: (event: EventPayload) => void;\n\n  readonly onLayout?: (event: EventPayload) => void;\n\n  readonly padding?: SpaceStep;\n\n  readonly radius?: RadiusStep;\n\n  readonly border?: boolean;\n\n  readonly scrollbarColor?: string;\n\n  readonly scrollbarWidth?: number;\n\n  readonly focusable?: boolean;\n\n  readonly label?: string;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n\n  readonly testId?: string;\n\n  readonly viewportTestId?: string;\n\n  readonly viewportRef?: Ref<PublicInstance>;\n}\n\nexport interface ScrollAreaViewportProps {\n  readonly axis?: ScrollSpec[\"axis\"];\n\n  readonly onScroll?: (event: EventPayload) => void;\n\n  readonly onScrollChange?: (event: EventPayload) => void;\n\n  readonly onLayout?: (event: EventPayload) => void;\n\n  readonly scrollbarColor?: string;\n\n  readonly scrollbarWidth?: number;\n\n  readonly focusable?: boolean;\n\n  readonly label?: string;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n\n  readonly ref?: Ref<PublicInstance>;\n}\n\nexport const renderScrollAreaViewport = (\n  {\n    axis,\n    onScroll,\n    onScrollChange,\n    onLayout,\n    scrollbarColor,\n    scrollbarWidth,\n    focusable,\n    label,\n    style,\n    className,\n    children,\n    testId,\n    ref,\n  }: ScrollAreaViewportProps,\n  part: ScrollPart,\n  instance?: (element: PublicInstance | null) => void,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const resolved = recipeTw.classesToStyle(\n    cn(scrollClass(axis ?? part.axis), className),\n    part.theme,\n  );\n  const focusablePort = focusable ?? part.focusable;\n  const viewport: StyleDesc = {\n    flexGrow: 1,\n    flexShrink: 1,\n    minHeight: 0,\n    minWidth: 0,\n    scrollbarColor: scrollbarColor ?? part.scrollbarColor,\n  };\n  if ((scrollbarWidth ?? part.scrollbarWidth) !== undefined) {\n    viewport.scrollbarWidth = scrollbarWidth ?? part.scrollbarWidth;\n  }\n\n  const name =\n    accessibleName(label ?? part.label) ??\n    (focusablePort\n      ? spokenWord(messages.scrollRegion, defaultMessages.scrollRegion)\n      : undefined);\n\n  const attach =\n    instance === undefined && ref === undefined\n      ? undefined\n      : mergeRefs(instance, ref);\n  return (\n    <div\n      ref={attach}\n      role={name === undefined ? undefined : \"group\"}\n      aria-label={name}\n      tabIndex={focusablePort ? 0 : undefined}\n      testId={testId}\n      onScroll={onScroll ?? part.onScroll}\n      onScrollChange={onScrollChange ?? part.onScrollChange}\n      onLayout={onLayout ?? part.onLayout}\n      onKeyDown={part.onScrollKey}\n      style={mergeStyle(\n        mergeStyle(\n          mergeStyle(viewport, resolved.style),\n\n          focusablePort ? controlRing(part.theme, {}) : undefined,\n        ),\n        style,\n      )}\n    >\n      {children}\n    </div>\n  );\n};\n\nconst useScrollKeyboard = (axis: ScrollSpec[\"axis\"], theme: PrimitiveTheme) => {\n  const { renderer } = useGpuix();\n  const target = useRef<number | null>(null);\n  const onKeyDown = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    const id = target.current;\n    if (id === null || renderer === null) return;\n\n    if (renderer.getFocusedElementId?.() !== id) return;\n    const box = renderer.getElementBounds?.(id);\n    if (box === null || box === undefined) return;\n    const offsets = renderer.getScrollOffset?.(id) ?? null;\n    Option.match(\n      scrollTarget(\n        event.key ?? \"\",\n        axis,\n        { x: offsets?.[0] ?? 0, y: offsets?.[1] ?? 0 },\n        {\n          line: rowHeightOf(theme),\n          page: { width: box.width, height: box.height },\n        },\n      ),\n      {\n        onNone: () => {},\n        onSome: (next) => {\n          event.preventDefault();\n          renderer.scrollTo?.(id, next.x, next.y);\n        },\n      },\n    );\n  };\n  return {\n    onKeyDown,\n    instance: (element: PublicInstance | null): void => {\n      target.current = element?.id ?? null;\n    },\n  };\n};\n\nexport const ScrollAreaViewport = (\n  props: ScrollAreaViewportProps,\n): ReactElement => {\n  const area = useScrollPart(\"ScrollAreaViewport\");\n  const messages = useMessages();\n  const keyboard = useScrollKeyboard(props.axis ?? area.axis, area.theme);\n  return renderScrollAreaViewport(\n    props,\n    { ...area, onScrollKey: keyboard.onKeyDown },\n    keyboard.instance,\n    messages,\n  );\n};\n\nconst composesItsOwnViewport = (children: ReactNode): boolean =>\n  Arr.some(Children.toArray(children), (child) => {\n    if (!isValidElement<{ readonly children?: ReactNode }>(child)) return false;\n    if (child.type === ScrollAreaViewport) return true;\n    return (\n      child.type === Fragment && composesItsOwnViewport(child.props.children)\n    );\n  });\n\nexport const renderScrollArea = (\n  {\n    style,\n    className,\n    children,\n    testId,\n    viewportTestId,\n    viewportRef,\n    padding,\n    radius,\n    border,\n    ...spec\n  }: ScrollAreaProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const part = scrollPart(spec, theme);\n  return (\n    <div\n      testId={testId}\n      style={mergeStyle(\n        recipeTw.classesToStyle(\n          cn(scrollFrameClass({ padding, radius, border }), className),\n          theme,\n        ).style,\n        style,\n      )}\n    >\n      <ScrollPartContext.Provider value={part}>\n        {composesItsOwnViewport(children) ? (\n          children\n        ) : (\n          <ScrollAreaViewport ref={viewportRef} testId={viewportTestId}>\n            {children}\n          </ScrollAreaViewport>\n        )}\n      </ScrollPartContext.Provider>\n    </div>\n  );\n};\n\nexport const ScrollArea = (props: ScrollAreaProps): ReactElement =>\n  renderScrollArea(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/Select.tsx",
      "target": "@ui/gpuixui/primitives/components/Select.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  Fragment,\n  useCallback,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport type { PublicInstance, StyleDesc } from \"@gpuix/react\";\nimport {\n  resolveStyle,\n  type FloatingAlign,\n  type FloatingSide,\n  type StateStyle,\n} from \"@gpuix/react/floating\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport { pipe } from \"effect\";\nimport {\n  Select as GpuixSelect,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectLabel,\n  SelectSeparator,\n  SelectTrigger,\n  SelectValue,\n  type SelectItemState,\n  type SelectTriggerState,\n} from \"@gpuix/react/select\";\nimport { accessibleName, visibleCopy } from \"../core/a11y.js\";\nimport { controlRing, keepContent } from \"../core/focus.js\";\nimport { revealScroll } from \"../core/scroll.js\";\nimport { storedList, storedText } from \"../core/stored.js\";\nimport type { ControlSpec } from \"../core/style.js\";\nimport { colorRole, controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { fieldVariant } from \"../core/variants.js\";\nimport { Icon } from \"../../icons/index.js\";\nimport { elevationShadow } from \"../../tokens/index.js\";\nimport {\n  selectContentClass,\n  selectItemClass,\n  selectItemRowClass,\n  selectLabelClass,\n  selectSeparatorClass,\n  selectTriggerClass,\n} from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  mergeStyle,\n  suppressFeedback,\n  valueChange,\n  type FieldHostProps,\n  type HelpProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport type { SelectItemState, SelectTriggerState };\n\nexport interface PublicInstanceHolder {\n  readonly current: PublicInstance | null;\n}\n\nexport interface SelectOption {\n  readonly value: string;\n  readonly label: string;\n  readonly disabled?: boolean;\n}\n\nexport interface SelectOptionGroup {\n  readonly label: string;\n  readonly options: ReadonlyArray<SelectOption>;\n}\n\nexport type SelectEntry = SelectOption | SelectOptionGroup;\n\nexport const isSelectGroup = (entry: SelectEntry): entry is SelectOptionGroup =>\n  \"options\" in entry;\n\nconst storedOption = (entry: unknown): Option.Option<SelectOption> => {\n  if (!P.isObject(entry)) return Option.none();\n  const value = storedText(entry[\"value\"]);\n  if (value === undefined) return Option.none();\n  return Option.some({\n    value,\n\n    label: storedText(entry[\"label\"]) ?? \"\",\n    disabled: entry[\"disabled\"] === true,\n  });\n};\n\nconst storedEntry = (entry: unknown): Option.Option<SelectEntry> => {\n  if (!P.isObject(entry) || !(\"options\" in entry)) return storedOption(entry);\n  const options = entry[\"options\"];\n\n  return Arr.isArray(options)\n    ? Option.some({\n        label: storedText(entry[\"label\"]) ?? \"\",\n        options: Arr.getSomes(Arr.map(options, storedOption)),\n      })\n    : Option.none();\n};\n\nexport const selectEntries = (value: unknown): ReadonlyArray<SelectEntry> =>\n  storedList(value, storedEntry);\n\nexport const selectOptions = (entries: unknown): ReadonlyArray<SelectOption> =>\n  Arr.flatMap(selectEntries(entries), (entry) =>\n    isSelectGroup(entry) ? entry.options : [entry],\n  );\n\nexport const selectValueLabel = (\n  entries: unknown,\n  value: Option.Option<string>,\n): Option.Option<string> =>\n  pipe(\n    value,\n    Option.flatMap((chosen) =>\n      Arr.findFirst(\n        selectOptions(entries),\n        (option) => option.value === chosen,\n      ),\n    ),\n    Option.flatMap((option) =>\n      Option.fromNullishOr(accessibleName(option.label)),\n    ),\n  );\n\nexport interface SelectProps\n  extends ValueChangeProps<string>, FieldHostProps, HelpProps {\n  readonly options: ReadonlyArray<SelectEntry>;\n\n  readonly value?: string;\n  readonly defaultValue?: string;\n\n  readonly selectedValue?: string;\n  readonly placeholder?: string;\n  readonly disabled?: boolean;\n\n  readonly open?: boolean;\n\n  readonly defaultOpen?: boolean;\n  readonly onOpenChange?: (open: boolean) => void;\n\n  readonly invalid?: boolean;\n\n  readonly size?: ControlSize;\n  readonly fullWidth?: boolean;\n\n  readonly side?: FloatingSide;\n\n  readonly align?: FloatingAlign;\n\n  readonly label?: string;\n\n  readonly style?: StyleDesc;\n\n  readonly triggerStyle?: StateStyle<SelectTriggerState>;\n\n  readonly itemStyle?: StateStyle<SelectItemState>;\n\n  readonly testId?: string;\n\n  readonly triggerTestId?: string;\n\n  readonly itemTestId?: (value: string) => string;\n}\n\nexport const SelectIndicator = ({\n  theme,\n}: {\n  readonly theme: PrimitiveTheme;\n}) => {\n  const capsule = colorRole(theme, \"primary\");\n  const ink = colorRole(theme, \"primaryForeground\");\n  const quiet = colorRole(theme, \"mutedForeground\");\n  if (theme.interaction === \"appkit\") {\n    return capsule === undefined || ink === undefined ? null : (\n      <div\n        style={{\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          width: 16,\n          height: 16,\n          flexShrink: 0,\n          borderRadius: 4,\n          backgroundColor: capsule,\n          pointerEvents: \"none\",\n        }}\n      >\n        <Icon name=\"chevrons-up-down\" size=\"xs\" color={ink} />\n      </div>\n    );\n  }\n  return quiet === undefined ? null : (\n    <Icon name=\"chevron-down\" size=\"sm\" color={quiet} />\n  );\n};\n\nexport const selectTriggerStyle = (\n  spec: ControlSpec,\n  theme: PrimitiveTheme,\n  caller?: StateStyle<SelectTriggerState>,\n): ((state: SelectTriggerState) => StyleDesc) => {\n  const resolved = recipeTw.classesToStyle(\n    selectTriggerClass({\n      size: spec.size,\n      invalid: spec.invalid,\n      fullWidth: spec.fullWidth,\n    }),\n    theme,\n  );\n  return (state) => {\n    let style = mergeStyle(resolved.style, keepContent);\n    if (theme.interaction === \"appkit\") {\n      style = mergeStyle(style, {\n        backgroundColor: colorRole(theme, \"card\"),\n        boxShadow: elevationShadow(theme, \"sm\"),\n      });\n    }\n\n    if (state.placeholder === true) {\n      style = mergeStyle(style, { color: colorRole(theme, \"placeholder\") });\n    }\n    if (spec.invalid === true) style = mergeStyle(style, resolved.invalid);\n    if (state.disabled === true) {\n      style = suppressFeedback(mergeStyle(style, resolved.disabled));\n    } else {\n      style = mergeStyle(style, controlRing(theme, { invalid: spec.invalid }));\n    }\n    return mergeStyle(style, resolveStyle(caller, state));\n  };\n};\n\nexport const selectContentStyle = (theme: PrimitiveTheme): StyleDesc =>\n  mergeStyle(recipeTw.classesToStyle(selectContentClass, theme).style, {\n    maxHeight: 320,\n    overflowY: \"scroll\",\n  });\n\nexport const selectLabelStyle = (theme: PrimitiveTheme): StyleDesc =>\n  recipeTw.classesToStyle(selectLabelClass, theme).style;\n\nexport const selectSeparatorStyle = (theme: PrimitiveTheme): StyleDesc =>\n  recipeTw.classesToStyle(selectSeparatorClass, theme).style;\n\nexport const selectItemStyle =\n  (\n    theme: PrimitiveTheme,\n    caller?: StateStyle<SelectItemState>,\n  ): ((state: SelectItemState) => StyleDesc) =>\n  (state) =>\n    mergeStyle(\n      recipeTw.classesToStyle(selectItemClass(state), theme).style,\n      resolveStyle(caller, state),\n    );\n\nexport const renderSelectRow = (\n  option: SelectOption,\n  theme: PrimitiveTheme,\n  state: SelectItemState,\n): ReactElement => {\n  const mark = colorRole(theme, \"popoverForeground\");\n  return (\n    <div\n      role=\"option\"\n      aria-label={accessibleName(option.label)}\n      aria-selected={state.selected}\n      style={recipeTw.classesToStyle(selectItemRowClass, theme).style}\n    >\n      {option.label}\n      {}\n      {state.selected && mark !== undefined ? (\n        <Icon name=\"check\" size=\"sm\" color={mark} />\n      ) : null}\n    </div>\n  );\n};\n\nconst RevealOnHighlight = ({\n  highlighted,\n  instance,\n  scroller,\n  children,\n}: {\n  readonly highlighted: boolean;\n  readonly instance: PublicInstanceHolder;\n  readonly scroller: PublicInstanceHolder;\n  readonly children: ReactNode;\n}): ReactElement => {\n  const { renderer } = useGpuix();\n  useLayoutEffect(() => {\n    if (!highlighted || renderer === null) return;\n    const row = instance.current;\n    const viewport = scroller.current;\n    if (row === null || viewport === null) return;\n    const rowBox = renderer.getElementBounds?.(row.id);\n    const viewportBox = renderer.getElementBounds?.(viewport.id);\n    const current = renderer.getScrollOffset?.(viewport.id);\n    if (!rowBox || !viewportBox || !current) return;\n    const [x, y] = current;\n    Option.match(revealScroll(rowBox, viewportBox, { x: x ?? 0, y: y ?? 0 }), {\n      onNone: () => {},\n      onSome: (next) => renderer.scrollTo?.(viewport.id, next.x, next.y),\n    });\n  }, [highlighted, instance, scroller, renderer]);\n  return <>{children}</>;\n};\n\nconst SelectRow = ({\n  option,\n  theme,\n  itemStyle,\n  testId,\n  scroller,\n}: {\n  readonly option: SelectOption;\n  readonly theme: PrimitiveTheme;\n  readonly itemStyle: StateStyle<SelectItemState> | undefined;\n  readonly testId: string | undefined;\n  readonly scroller: PublicInstanceHolder;\n}): ReactElement => {\n  const instance = useRef<PublicInstance | null>(null);\n\n  const attach = useCallback((next: PublicInstance | null) => {\n    instance.current = next;\n  }, []);\n  return (\n    <SelectItem\n      ref={attach}\n      value={option.value}\n      disabled={option.disabled}\n      testId={testId}\n      style={selectItemStyle(theme, itemStyle)}\n    >\n      {(state) => (\n        <RevealOnHighlight\n          highlighted={state.highlighted}\n          instance={instance}\n          scroller={scroller}\n        >\n          {renderSelectRow(option, theme, state)}\n        </RevealOnHighlight>\n      )}\n    </SelectItem>\n  );\n};\n\nexport const renderSelect = (\n  {\n    options,\n    value,\n    defaultValue,\n    selectedValue,\n    onValueChange,\n    onChange,\n    placeholder,\n    disabled,\n    open,\n    onOpenChange,\n    invalid,\n    size: requestedSize,\n    fullWidth,\n    side,\n    align,\n    label,\n    description,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    style,\n    triggerStyle,\n    itemStyle,\n    testId,\n    triggerTestId,\n    itemTestId,\n  }: SelectProps,\n  theme: PrimitiveTheme,\n  scroller: PublicInstanceHolder = { current: null },\n): ReactElement => {\n  const spec: ControlSpec = {\n    variant: fieldVariant,\n    size: controlSize(requestedSize),\n    invalid,\n    fullWidth,\n  };\n  const entries = selectEntries(options);\n  const items = Arr.map(selectOptions(entries), (option) => ({\n    value: option.value,\n    label: option.label,\n    textValue: option.label,\n    disabled: option.disabled === true,\n  }));\n  const renderItem = (option: SelectOption): ReactElement => (\n    <SelectRow\n      key={option.value}\n      option={option}\n      theme={theme}\n      itemStyle={itemStyle}\n      testId={itemTestId?.(option.value)}\n      scroller={scroller}\n    />\n  );\n\n  const announced = Option.getOrUndefined(\n    selectValueLabel(\n      entries,\n      Option.fromNullishOr(selectedValue ?? value ?? defaultValue),\n    ),\n  );\n  return (\n    <GpuixSelect\n      items={items}\n      value={value}\n      defaultValue={defaultValue}\n      onValueChange={valueChange({ onValueChange, onChange })}\n      open={open}\n      onOpenChange={onOpenChange}\n      disabled={disabled}\n      testId={testId}\n      style={mergeStyle(\n        { width: fullWidth === true ? \"100%\" : undefined },\n        style,\n      )}\n    >\n      <SelectTrigger\n        ref={ref}\n        disabled={disabled}\n        testId={triggerTestId}\n        role=\"combobox\"\n        aria-label={accessibleName(label)}\n        aria-description={accessibleName(description)}\n        aria-id={controlId}\n        onLayout={onLayout}\n        aria-expanded={open}\n        aria-valuetext={announced}\n        style={selectTriggerStyle(spec, theme, triggerStyle)}\n      >\n        <SelectValue placeholder={placeholder} />\n        <SelectIndicator theme={theme} />\n      </SelectTrigger>\n      <SelectContent\n        ref={scroller}\n        side={side}\n        align={align}\n        role=\"listbox\"\n        style={selectContentStyle(theme)}\n      >\n        {Arr.map(entries, (entry, index) => {\n          if (!isSelectGroup(entry)) return renderItem(entry);\n\n          const caption = visibleCopy(entry.label);\n          return (\n            <Fragment key={`group-${index}`}>\n              {index === 0 ? null : (\n                <SelectSeparator style={selectSeparatorStyle(theme)} />\n              )}\n              {}\n              <SelectGroup role=\"group\" aria-label={accessibleName(caption)}>\n                {caption === undefined ? null : (\n                  <SelectLabel style={selectLabelStyle(theme)}>\n                    {caption}\n                  </SelectLabel>\n                )}\n                {}\n                {...Arr.map(entry.options, renderItem)}\n              </SelectGroup>\n            </Fragment>\n          );\n        })}\n      </SelectContent>\n    </GpuixSelect>\n  );\n};\n\nexport const Select = (props: SelectProps): ReactElement => {\n  const theme = useTheme();\n  const scroller = useRef<PublicInstance | null>(null);\n\n  const [held, setHeld] = useState(props.defaultOpen === true);\n\n  const [heldValue, setHeldValue] = useState<string | undefined>(\n    props.defaultValue,\n  );\n  const open = props.open ?? held;\n  const notify = valueChange(props);\n  return renderSelect(\n    {\n      ...props,\n      open,\n      selectedValue: props.selectedValue ?? props.value ?? heldValue,\n      onValueChange: (next) => {\n        if (props.value === undefined) setHeldValue(next);\n        notify?.(next);\n      },\n\n      onChange: undefined,\n      onOpenChange: (next) => {\n        if (props.open === undefined) setHeld(next);\n        props.onOpenChange?.(next);\n      },\n    },\n    theme,\n    scroller,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Separator.tsx",
      "target": "@ui/gpuixui/primitives/components/Separator.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport { visibleCopy } from \"../core/a11y.js\";\nimport { orientationOf } from \"../core/keyboard.js\";\nimport type { RuleSpec } from \"../core/style.js\";\nimport { spaceStep, spaceValue } from \"../core/theme.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { separatorClass, separatorGroupClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, type ElementHostProps } from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\nimport { Text } from \"./Text.js\";\n\nexport interface SeparatorProps extends RuleSpec {\n  readonly label?: string;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n}\n\nexport const renderSeparator = (\n  { orientation, inset, label, style, className, testId }: SeparatorProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const axis = orientationOf(orientation, \"horizontal\");\n  const classes = cn(separatorClass(axis, inset), className);\n  const rule = mergeStyle(recipeTw.classesToStyle(classes, theme).style, style);\n  const caption = visibleCopy(label);\n  if (caption === undefined || axis === \"vertical\") {\n    return <div testId={testId} style={rule} />;\n  }\n  const grown = mergeStyle(\n    recipeTw.classesToStyle(cn(classes, \"flex-grow\"), theme).style,\n    style,\n  );\n  const row = recipeTw.classesToStyle(separatorGroupClass, theme).style;\n  return (\n    <div testId={testId} style={row}>\n      <div style={grown} />\n      <Text role=\"caption\" tone=\"muted\">\n        {caption}\n      </Text>\n      <div style={grown} />\n    </div>\n  );\n};\n\nexport const Separator = (props: SeparatorProps): ReactElement =>\n  renderSeparator(props, useTheme());\n\nexport interface SpacerProps extends ElementHostProps {\n  readonly size?: keyof PrimitiveTheme[\"spacing\"];\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n}\n\nexport const renderSpacer = (\n  {\n    size,\n    style,\n    className,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n  }: SpacerProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const step = spaceStep(theme, size);\n  const fixed: StyleDesc =\n    step === undefined\n      ? { flexGrow: 1, flexBasis: 0 }\n      : {\n          flexShrink: 0,\n          flexGrow: 0,\n          width: spaceValue(theme, step),\n          height: spaceValue(theme, step),\n        };\n  return (\n    <div\n      ref={ref}\n      aria-id={controlId}\n      onLayout={onLayout}\n      testId={testId}\n      style={mergeStyle(\n        mergeStyle(fixed, recipeTw.classesToStyle(cn(className), theme).style),\n        style,\n      )}\n    />\n  );\n};\n\nexport const Spacer = (props: SpacerProps): ReactElement =>\n  renderSpacer(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/Skeleton.tsx",
      "target": "@ui/gpuixui/primitives/components/Skeleton.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { cn } from \"../../tw/index.js\";\nimport {\n  PULSE_INTERVAL_MS,\n  PULSE_STEPS,\n  pulseOpacity,\n  skeletonSize,\n  type SkeletonSpec,\n} from \"../core/loading.js\";\nimport { reducedMotionOf, stillFrame } from \"../core/motion.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { skeletonClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, useStep, type ElementHostProps } from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface SkeletonProps extends SkeletonSpec, ElementHostProps {\n  readonly reducedMotion?: boolean;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderSkeleton = (\n  {\n    style,\n    className,\n    testId,\n    reducedMotion,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    ...spec\n  }: SkeletonProps,\n  theme: PrimitiveTheme,\n  step: number,\n): ReactElement => (\n  <div\n    ref={ref}\n    aria-id={controlId}\n    onLayout={onLayout}\n    testId={testId}\n    style={mergeStyle(\n      mergeStyle(\n        recipeTw.classesToStyle(cn(skeletonClass(spec), className), theme)\n          .style,\n        {\n          ...skeletonSize(spec, theme),\n          opacity: pulseOpacity(stillFrame(step, reducedMotion)),\n        },\n      ),\n      style,\n    )}\n  />\n);\n\nexport const Skeleton = (props: SkeletonProps): ReactElement => {\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  return renderSkeleton(\n    { ...props, reducedMotion },\n    useTheme(),\n\n    useStep(PULSE_STEPS, PULSE_INTERVAL_MS, !reducedMotion),\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Slider.tsx",
      "target": "@ui/gpuixui/primitives/components/Slider.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  Children,\n  Fragment,\n  createContext,\n  isValidElement,\n  useContext,\n  useRef,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type {\n  EventPayload,\n  KeyEvent,\n  NativeRenderer,\n  PublicInstance,\n  StyleDesc,\n} from \"@gpuix/react\";\nimport { useGpuix } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport * as Arr from \"effect/Array\";\nimport { accessibleName, spokenText } from \"../core/a11y.js\";\nimport {\n  defaultMessages,\n  rangeThumbNames,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport { isPlainKey } from \"../core/keyboard.js\";\nimport { primaryButton } from \"../core/pointer.js\";\nimport * as Option from \"effect/Option\";\nimport { cn } from \"../../tw/index.js\";\nimport {\n  clampValue,\n  decideValueKey,\n  moveSpanEnd,\n  nearestEnd,\n  normalizeSpan,\n  pointerValue,\n  spanFrom,\n  storedRange,\n  valueFraction,\n  verticalPointerValue,\n  type ValueRange,\n  type ValueSpan,\n} from \"../core/value.js\";\nimport { sliderInset, type SliderOrientation } from \"../core/style.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport {\n  sliderFillClass,\n  sliderRailClass,\n  sliderThumbClass,\n  sliderTrackClass,\n} from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  composeEvent,\n  mergeRefs,\n  mergeStyle,\n  useControlState,\n  useFocus,\n  valueChange,\n  type FieldHostProps,\n  type FocusState,\n  type HelpProps,\n  type InteractionProps,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface SliderProps\n  extends\n    ValueChangeProps<number>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly value?: number;\n  readonly defaultValue?: number;\n\n  readonly min?: number;\n\n  readonly max?: number;\n\n  readonly step?: number;\n\n  readonly size?: ControlSize;\n  readonly disabled?: boolean;\n\n  readonly invalid?: boolean;\n\n  readonly label?: string;\n\n  readonly formatValue?: (value: number) => string;\n\n  readonly onValueCommit?: (value: number) => void;\n\n  readonly orientation?: SliderOrientation;\n\n  readonly style?: StateStyle<SliderState>;\n\n  readonly className?: string;\n\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport interface RangeSliderProps\n  extends\n    ValueChangeProps<readonly [number, number]>,\n    InteractionProps,\n    FieldHostProps,\n    HelpProps {\n  readonly value?: readonly [number, number];\n  readonly defaultValue?: readonly [number, number];\n\n  readonly min?: number;\n\n  readonly max?: number;\n\n  readonly step?: number;\n  readonly size?: ControlSize;\n  readonly disabled?: boolean;\n  readonly invalid?: boolean;\n\n  readonly label?: string;\n\n  readonly formatValue?: (value: number) => string;\n\n  readonly onValueCommit?: (span: readonly [number, number]) => void;\n\n  readonly orientation?: SliderOrientation;\n\n  readonly style?: StateStyle<SliderState>;\n\n  readonly className?: string;\n\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const sliderRange = (props: {\n  readonly min?: number;\n  readonly max?: number;\n  readonly step?: number;\n}): ValueRange =>\n  storedRange(props, {\n    min: 0,\n    max: 100,\n    step: 1,\n  });\n\nexport interface SliderState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly invalid: boolean;\n}\n\nconst sliderState = (\n  props: { readonly disabled?: boolean; readonly invalid?: boolean },\n  focused: boolean,\n): SliderState => ({\n  disabled: props.disabled === true,\n  focused,\n  invalid: props.invalid === true,\n});\n\nexport const sliderValueText = (\n  value: number,\n  formatValue?: (value: number) => string,\n): string =>\n  spokenText(Option.fromNullishOr(formatValue?.(value)), String(value));\n\nexport interface SliderHandlers {\n  readonly onValueChange: (next: number) => void;\n  readonly registerTrack: (instance: PublicInstance | null) => void;\n  readonly onPointerDown: (event: EventPayload) => void;\n  readonly onPointerMove: (event: EventPayload) => void;\n  readonly onPointerUp: () => void;\n}\n\nconst fillStyle = (\n  fraction: number,\n  invalid: boolean,\n  theme: PrimitiveTheme,\n  orientation: SliderOrientation,\n): StyleDesc => {\n  const share = `${fraction * 100}%`;\n  return mergeStyle(\n    recipeTw.classesToStyle(sliderFillClass(invalid, orientation), theme).style,\n    orientation === \"vertical\" ? { height: share } : { width: share },\n  );\n};\n\nconst thumbStyle = (\n  size: ControlSize,\n  invalid: boolean,\n  theme: PrimitiveTheme,\n  orientation: SliderOrientation,\n): StyleDesc =>\n  recipeTw.classesToStyle(sliderThumbClass(size, invalid, orientation), theme)\n    .style;\n\ninterface ThumbPart {\n  readonly label: string | undefined;\n  readonly valueText: string;\n  readonly tabIndex: number | undefined;\n  readonly ring: boolean;\n  readonly ref: (instance: PublicInstance | null) => void;\n  readonly \"aria-id\"?: string;\n  readonly onLayout?: ((event: EventPayload) => void) | undefined;\n  readonly onClick: ((event: EventPayload) => void) | undefined;\n  readonly onFocus: ((event: EventPayload) => void) | undefined;\n  readonly onBlur: ((event: EventPayload) => void) | undefined;\n  readonly onKeyDown: ((event: KeyEvent) => void) | undefined;\n  readonly onKeyUp: ((event: KeyEvent) => void) | undefined;\n}\n\ninterface SliderPart {\n  readonly theme: PrimitiveTheme;\n  readonly size: ControlSize;\n  readonly invalid: boolean;\n\n  readonly orientation: SliderOrientation;\n\n  readonly fractions: ReadonlyArray<number>;\n\n  readonly thumbs: ReadonlyArray<ThumbPart | null>;\n\n  readonly press: ((event: EventPayload) => void) | undefined;\n}\n\nconst SliderPartContext = createContext<SliderPart | null>(null);\n\nconst useSliderPart = (name: string): SliderPart => {\n  const part = useContext(SliderPartContext);\n  if (part === null) {\n    throw new Error(`${name} must be used inside Slider or RangeSlider`);\n  }\n  return part;\n};\n\nexport interface SliderTrackProps {\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n}\n\nconst flattenParts = (children: ReactNode): ReadonlyArray<ReactNode> =>\n  Arr.flatMap(Children.toArray(children), (child) =>\n    isValidElement<{ readonly children?: ReactNode }>(child) &&\n    child.type === Fragment\n      ? flattenParts(child.props.children)\n      : [child],\n  );\n\nexport const renderSliderTrack = (\n  { children, className, style, testId }: SliderTrackProps,\n  part: SliderPart,\n): ReactElement => (\n  <div\n    testId={testId}\n    style={mergeStyle(\n      recipeTw.classesToStyle(\n        cn(sliderRailClass(part.orientation), className),\n        part.theme,\n      ).style,\n      style,\n    )}\n  >\n    {part.orientation === \"vertical\"\n      ? Arr.reverse(flattenParts(children))\n      : children}\n  </div>\n);\n\nexport const SliderTrack = (props: SliderTrackProps): ReactElement =>\n  renderSliderTrack(props, useSliderPart(\"SliderTrack\"));\n\nexport interface SliderIndicatorProps {\n  readonly index?: number;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n}\n\nexport const renderSliderIndicator = (\n  { index = 0, className, style }: SliderIndicatorProps,\n  part: SliderPart,\n): ReactElement => {\n  const to = part.fractions[index] ?? 0;\n  const from = index === 0 ? 0 : (part.fractions[index - 1] ?? 0);\n  return (\n    <div\n      style={mergeStyle(\n        mergeStyle(\n          fillStyle(to - from, part.invalid, part.theme, part.orientation),\n          recipeTw.classesToStyle(className, part.theme).style,\n        ),\n        style,\n      )}\n    />\n  );\n};\n\nexport const SliderIndicator = (props: SliderIndicatorProps): ReactElement =>\n  renderSliderIndicator(props, useSliderPart(\"SliderIndicator\"));\n\nexport interface SliderThumbProps {\n  readonly index?: 0 | 1;\n\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n}\n\nexport const renderSliderThumb = (\n  { index = 0, children, className, style }: SliderThumbProps,\n  part: SliderPart,\n): ReactElement => {\n  const thumb = part.thumbs[index] ?? null;\n  let resolved = thumbStyle(\n    part.size,\n    part.invalid,\n    part.theme,\n    part.orientation,\n  );\n  if (thumb?.ring === true) {\n    resolved = mergeStyle(\n      resolved,\n      controlRing(part.theme, { invalid: part.invalid }),\n    );\n  }\n  resolved = mergeStyle(\n    resolved,\n    recipeTw.classesToStyle(className, part.theme).style,\n  );\n  resolved = mergeStyle(resolved, style);\n  if (thumb === null) {\n    return (\n      <div onClick={part.press} style={resolved}>\n        {children}\n      </div>\n    );\n  }\n  return (\n    <div\n      ref={thumb.ref}\n      role=\"slider\"\n      aria-label={thumb.label}\n      aria-id={thumb[\"aria-id\"]}\n      aria-valuetext={thumb.valueText}\n      tabIndex={thumb.tabIndex}\n      onClick={thumb.onClick}\n      onLayout={thumb.onLayout}\n      onFocus={thumb.onFocus}\n      onBlur={thumb.onBlur}\n      onKeyDown={thumb.onKeyDown}\n      onKeyUp={thumb.onKeyUp}\n      style={resolved}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport const SliderThumb = (props: SliderThumbProps): ReactElement =>\n  renderSliderThumb(props, useSliderPart(\"SliderThumb\"));\n\nexport const renderSlider = (\n  props: SliderProps,\n  theme: PrimitiveTheme,\n  state: ValueState<number>,\n  focus: FocusState,\n  handlers: SliderHandlers,\n): ReactElement => {\n  const range = sliderRange(props);\n  const value = clampValue(state.current, range);\n  const size = controlSize(props.size);\n  const invalid = props.invalid === true;\n  const orientation = props.orientation ?? \"horizontal\";\n  const resolved = recipeTw.classesToStyle(\n    cn(sliderTrackClass(size, orientation), props.className),\n    theme,\n  );\n  let track = resolved.style;\n  if (props.disabled === true) track = mergeStyle(track, resolved.disabled);\n  track = mergeStyle(\n    track,\n    controlRing(theme, { disabled: props.disabled, invalid }),\n  );\n  track = mergeStyle(\n    track,\n    resolveStyle(props.style, sliderState(props, focus.focused)),\n  );\n  const part: SliderPart = {\n    theme,\n    size,\n    invalid,\n    orientation,\n    fractions: [valueFraction(value, range)],\n    thumbs: [null],\n    press: props.disabled === true ? undefined : props.onClick,\n  };\n  return (\n    <div\n      ref={mergeRefs(handlers.registerTrack, props.ref)}\n      role=\"slider\"\n      aria-label={accessibleName(props.label)}\n      aria-description={accessibleName(props.description)}\n      aria-id={props[\"aria-id\"]}\n      aria-valuetext={sliderValueText(value, props.formatValue)}\n      tabIndex={props.disabled === true ? undefined : 0}\n      testId={props.testId}\n      onLayout={props.onLayout}\n      onClick={props.disabled === true ? undefined : props.onClick}\n      onMouseDown={handlers.onPointerDown}\n      onMouseMove={handlers.onPointerMove}\n      onMouseUp={handlers.onPointerUp}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, (event) => {\n        if (props.disabled === true || !isPlainKey(event.modifiers)) return;\n        Option.match(decideValueKey(value, range, event.key ?? \"\"), {\n          onNone: () => {},\n\n          onSome: (next) => {\n            if (next !== value) handlers.onValueChange(next);\n          },\n        });\n      })}\n      onKeyUp={props.onKeyUp}\n      onFocus={composeEvent<EventPayload>(props.onFocus, focus.onFocus)}\n      onBlur={composeEvent<EventPayload>(props.onBlur, focus.onBlur)}\n      style={track}\n    >\n      <SliderPartContext.Provider value={part}>\n        {props.children ??\n          renderSliderTrack(\n            {\n              children: (\n                <>\n                  {renderSliderIndicator({}, part)}\n                  {renderSliderThumb({}, part)}\n                </>\n              ),\n            },\n            part,\n          )}\n      </SliderPartContext.Provider>\n    </div>\n  );\n};\n\nexport interface RangeSliderFocus {\n  readonly focusedIndex: Option.Option<0 | 1>;\n  readonly onFocus: (index: 0 | 1) => void;\n  readonly onBlur: (index: 0 | 1) => void;\n}\n\nexport interface RangeSliderHandlers {\n  readonly onSpanChange: (next: readonly [number, number]) => void;\n  readonly registerTrack: (instance: PublicInstance | null) => void;\n  readonly registerThumb: (\n    index: 0 | 1,\n  ) => (instance: PublicInstance | null) => void;\n  readonly onPointerDown: (event: EventPayload) => void;\n  readonly onPointerMove: (event: EventPayload) => void;\n  readonly onPointerUp: () => void;\n}\n\nconst spanOf = (\n  values: readonly [number, number],\n  range: ValueRange,\n): ValueSpan => normalizeSpan({ start: values[0], end: values[1] }, range);\n\nconst spanTuple = (span: ValueSpan): readonly [number, number] => [\n  span.start,\n  span.end,\n];\n\nexport const renderRangeSlider = (\n  props: RangeSliderProps,\n  theme: PrimitiveTheme,\n  state: ValueState<readonly [number, number]>,\n  focus: RangeSliderFocus,\n  handlers: RangeSliderHandlers,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const range = sliderRange(props);\n  const span = spanOf(state.current, range);\n  const size = controlSize(props.size);\n  const invalid = props.invalid === true;\n  const orientation = props.orientation ?? \"horizontal\";\n\n  const labels = rangeThumbNames(\n    accessibleName(props.label),\n    messages.rangeEnd,\n  );\n\n  const addressedIndex = Option.getOrElse(focus.focusedIndex, () => 0);\n  const thumbPart = (index: 0 | 1): ThumbPart => {\n    const value = index === 0 ? span.start : span.end;\n    const addressed = index === addressedIndex;\n    const register = handlers.registerThumb(index);\n    return {\n      label: labels[index],\n      valueText: sliderValueText(value, props.formatValue),\n      tabIndex: props.disabled === true ? undefined : 0,\n      ring: props.disabled !== true,\n      ref: addressed ? mergeRefs(register, props.ref) : register,\n      \"aria-id\": addressed ? props[\"aria-id\"] : undefined,\n      onLayout: addressed ? props.onLayout : undefined,\n      onClick: props.disabled === true ? undefined : props.onClick,\n      onFocus: composeEvent<EventPayload>(props.onFocus, () =>\n        focus.onFocus(index),\n      ),\n      onBlur: composeEvent<EventPayload>(props.onBlur, () =>\n        focus.onBlur(index),\n      ),\n      onKeyDown: composeEvent<KeyEvent>(props.onKeyDown, (event) => {\n        if (props.disabled === true || !isPlainKey(event.modifiers)) return;\n        const end = index === 0 ? \"start\" : \"end\";\n        Option.match(decideValueKey(value, range, event.key ?? \"\"), {\n          onNone: () => {},\n          onSome: (next) => {\n            const moved = moveSpanEnd(span, end, next, range);\n            if (moved.start !== span.start || moved.end !== span.end) {\n              handlers.onSpanChange(spanTuple(moved));\n            }\n          },\n        });\n      }),\n      onKeyUp: props.onKeyUp,\n    };\n  };\n  const resolved = recipeTw.classesToStyle(\n    cn(sliderTrackClass(size, orientation), props.className),\n    theme,\n  );\n  let track = resolved.style;\n  if (props.disabled === true) track = mergeStyle(track, resolved.disabled);\n  track = mergeStyle(\n    track,\n    resolveStyle(\n      props.style,\n      sliderState(props, Option.isSome(focus.focusedIndex)),\n    ),\n  );\n  const part: SliderPart = {\n    theme,\n    size,\n    invalid,\n    orientation,\n    fractions: [\n      valueFraction(span.start, range),\n      valueFraction(span.end, range),\n    ],\n    thumbs: [thumbPart(0), thumbPart(1)],\n    press: props.disabled === true ? undefined : props.onClick,\n  };\n  return (\n    <div\n      ref={handlers.registerTrack}\n      role=\"group\"\n      aria-label={accessibleName(props.label)}\n      aria-description={accessibleName(props.description)}\n      testId={props.testId}\n      onClick={props.disabled === true ? undefined : props.onClick}\n      onMouseDown={handlers.onPointerDown}\n      onMouseMove={handlers.onPointerMove}\n      onMouseUp={handlers.onPointerUp}\n      style={track}\n    >\n      <SliderPartContext.Provider value={part}>\n        {props.children ??\n          renderSliderTrack(\n            {\n              children: (\n                <>\n                  {renderSliderIndicator({ index: 0 }, part)}\n                  {renderSliderThumb({ index: 0 }, part)}\n                  {renderSliderIndicator({ index: 1 }, part)}\n                  {renderSliderThumb({ index: 1 }, part)}\n                </>\n              ),\n            },\n            part,\n          )}\n      </SliderPartContext.Provider>\n    </div>\n  );\n};\n\nconst pointerOf = (\n  event: EventPayload,\n  renderer: NativeRenderer | null,\n  trackId: number | null,\n  range: ValueRange,\n  inset: number,\n  orientation: SliderOrientation,\n): Option.Option<number> => {\n  if (trackId === null) return Option.none();\n  const box = renderer?.getElementBounds?.(trackId);\n  if (box === null || box === undefined) return Option.none();\n  return Option.some(\n    clampValue(\n      orientation === \"vertical\"\n        ? verticalPointerValue(event.y ?? 0, box, range, inset)\n        : pointerValue(event.x ?? 0, box, range, inset),\n      range,\n    ),\n  );\n};\n\nexport const Slider = (props: SliderProps): ReactElement => {\n  const theme = useTheme();\n  const { renderer } = useGpuix();\n  const focus = useFocus();\n  const range = sliderRange(props);\n  const trackId = useRef<number | null>(null);\n  const dragging = useRef(false);\n  const state = useControlState(\n    props.value,\n    clampValue(props.defaultValue ?? range.min, range),\n    valueChange(props),\n  );\n  const inset = sliderInset(controlSize(props.size), theme);\n  const orientation = props.orientation ?? \"horizontal\";\n\n  const gesture = useRef<Option.Option<number>>(Option.none());\n  const report = (next: number): void => {\n    gesture.current = Option.some(next);\n    state.set(next);\n  };\n  const endGesture = (): void => {\n    Option.match(gesture.current, {\n      onNone: () => {},\n      onSome: (value) => props.onValueCommit?.(value),\n    });\n    gesture.current = Option.none();\n  };\n  const apply = (event: EventPayload): void =>\n    Option.match(\n      pointerOf(event, renderer, trackId.current, range, inset, orientation),\n      {\n        onNone: () => {},\n        onSome: report,\n      },\n    );\n  return renderSlider(props, theme, state.value, focus, {\n    onValueChange: (next) => {\n      state.set(next);\n      props.onValueCommit?.(next);\n    },\n    registerTrack: (instance) => {\n      trackId.current = instance?.id ?? null;\n    },\n    onPointerDown: (event) => {\n      if (props.disabled === true || !primaryButton(event)) return;\n      dragging.current = true;\n      apply(event);\n    },\n    onPointerMove: (event) => {\n      if (props.disabled === true || !dragging.current) return;\n      if (event.pressedButton !== 0) {\n        dragging.current = false;\n        endGesture();\n        return;\n      }\n      apply(event);\n    },\n    onPointerUp: () => {\n      dragging.current = false;\n      endGesture();\n    },\n  });\n};\n\nexport const RangeSlider = (props: RangeSliderProps): ReactElement => {\n  const theme = useTheme();\n  const { renderer } = useGpuix();\n  const range = sliderRange(props);\n  const initial = spanOf(\n    [\n      props.defaultValue?.[0] ?? range.min,\n      props.defaultValue?.[1] ?? range.max,\n    ] as const,\n    range,\n  );\n  const trackId = useRef<number | null>(null);\n  const thumbIds = useRef<Map<0 | 1, number>>(new Map());\n\n  const anchor = useRef<Option.Option<number>>(Option.none());\n\n  const followedEnd = useRef<\"start\" | \"end\">(\"start\");\n  const dragging = useRef(false);\n  const [focusedIndex, setFocusedIndex] = useState<Option.Option<0 | 1>>(\n    Option.none(),\n  );\n  const state = useControlState<readonly [number, number]>(\n    props.value,\n    [initial.start, initial.end],\n    valueChange(props),\n  );\n  const inset = sliderInset(controlSize(props.size), theme);\n  const orientation = props.orientation ?? \"horizontal\";\n  const focusThumb = (index: 0 | 1): void => {\n    Option.match(Option.fromNullishOr(thumbIds.current.get(index)), {\n      onNone: () => {},\n      onSome: (id) => renderer?.focusElement?.(id),\n    });\n    setFocusedIndex(Option.some(index));\n  };\n\n  const gesture = useRef<Option.Option<readonly [number, number]>>(\n    Option.none(),\n  );\n  const endGesture = (): void => {\n    Option.match(gesture.current, {\n      onNone: () => {},\n      onSome: (span) => props.onValueCommit?.(span),\n    });\n    gesture.current = Option.none();\n  };\n  const change = (next: ValueSpan): void => {\n    const span = spanOf(state.value.current, range);\n    if (next.start !== span.start || next.end !== span.end) {\n      gesture.current = Option.some(spanTuple(next));\n      state.set(spanTuple(next));\n    }\n  };\n  return renderRangeSlider(\n    props,\n    theme,\n    state.value,\n    {\n      focusedIndex,\n      onFocus: (index) => setFocusedIndex(Option.some(index)),\n      onBlur: (index) =>\n        setFocusedIndex((current) =>\n          Option.filter(current, (focused) => focused !== index),\n        ),\n    },\n    {\n      onSpanChange: (next) => {\n        state.set(next);\n        props.onValueCommit?.(next);\n      },\n      registerTrack: (instance) => {\n        trackId.current = instance?.id ?? null;\n      },\n      registerThumb: (index) => (instance) => {\n        if (instance === null) thumbIds.current.delete(index);\n        else thumbIds.current.set(index, instance.id);\n      },\n      onPointerDown: (event) => {\n        if (props.disabled === true || !primaryButton(event)) return;\n        const span = spanOf(state.value.current, range);\n        Option.match(\n          pointerOf(\n            event,\n            renderer,\n            trackId.current,\n            range,\n            inset,\n            orientation,\n          ),\n          {\n            onNone: () => {},\n            onSome: (value) => {\n              const end = nearestEnd(value, span);\n              const held =\n                span.start === span.end\n                  ? span.start\n                  : span[end === \"start\" ? \"end\" : \"start\"];\n              anchor.current = Option.some(held);\n              followedEnd.current = end;\n              dragging.current = true;\n              change(spanFrom(held, value, range));\n              focusThumb(end === \"start\" ? 0 : 1);\n            },\n          },\n        );\n      },\n      onPointerMove: (event) => {\n        if (props.disabled === true || !dragging.current) return;\n        if (event.pressedButton !== 0) {\n          dragging.current = false;\n          anchor.current = Option.none();\n          endGesture();\n          return;\n        }\n        Option.match(\n          pointerOf(\n            event,\n            renderer,\n            trackId.current,\n            range,\n            inset,\n            orientation,\n          ),\n          {\n            onNone: () => {},\n            onSome: (value) =>\n              Option.match(anchor.current, {\n                onNone: () => {},\n                onSome: (held) => {\n                  change(spanFrom(held, value, range));\n\n                  const target = clampValue(value, range);\n                  if (target === held) return;\n                  const end = target < held ? \"start\" : \"end\";\n                  if (end === followedEnd.current) return;\n                  followedEnd.current = end;\n                  focusThumb(end === \"start\" ? 0 : 1);\n                },\n              }),\n          },\n        );\n      },\n      onPointerUp: () => {\n        dragging.current = false;\n        anchor.current = Option.none();\n        endGesture();\n      },\n    },\n    useMessages(),\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Spinner.tsx",
      "target": "@ui/gpuixui/primitives/components/Spinner.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport * as Arr from \"effect/Array\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { iconPixels } from \"../../icons/index.js\";\nimport { cn } from \"../../tw/index.js\";\nimport {\n  SPINNER_RING_TICKS,\n  STEP_INTERVAL_MS,\n  spinnerTickStyle,\n  spinnerTicks,\n  type SpinnerAccent,\n} from \"../core/loading.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport {\n  busyWord,\n  defaultMessages,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\nimport { reducedMotionOf, stillFrame } from \"../core/motion.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { spinnerGroupClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, useStep, type ElementHostProps } from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface SpinnerProps extends ElementHostProps {\n  readonly size?: keyof PrimitiveTheme[\"iconSize\"] | number;\n\n  readonly accent?: SpinnerAccent;\n\n  readonly label?: string;\n\n  readonly reducedMotion?: boolean;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const renderSpinner = (\n  {\n    size,\n    accent = \"primary\",\n    label,\n    reducedMotion,\n    style,\n    className,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n  }: SpinnerProps,\n  theme: PrimitiveTheme,\n  step: number,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const side = iconPixels(size ?? \"md\");\n  const ring = mergeStyle(\n    recipeTw.classesToStyle(cn(spinnerGroupClass, className), theme).style,\n    mergeStyle({ width: side, height: side }, style),\n  );\n\n  const name = accessibleName(label ?? busyWord(messages.loading));\n  return (\n    <div\n      ref={ref}\n      role={name === undefined ? \"presentation\" : \"status\"}\n      aria-label={name}\n      aria-id={controlId}\n      onLayout={onLayout}\n      testId={testId}\n      style={ring}\n    >\n      {Arr.map(\n        spinnerTicks(stillFrame(step, reducedMotion), side),\n        (tick, index) => (\n          <div\n            key={index}\n            style={spinnerTickStyle({ size: side, accent }, tick, theme)}\n          />\n        ),\n      )}\n    </div>\n  );\n};\n\nexport const Spinner = (props: SpinnerProps): ReactElement => {\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  return renderSpinner(\n    { ...props, reducedMotion },\n    useTheme(),\n\n    useStep(SPINNER_RING_TICKS, STEP_INTERVAL_MS, !reducedMotion),\n    useMessages(),\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Stack.tsx",
      "target": "@ui/gpuixui/primitives/components/Stack.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport type { GridSpec, StackSpec } from \"../core/layout.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { gridClass, stackClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle, type ElementHostProps } from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface StackProps extends StackSpec, ElementHostProps {\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const renderStack = (\n  {\n    style,\n    className,\n    children,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    ...spec\n  }: StackProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const resolved = recipeTw.classesToStyle(\n    cn(stackClass(spec), className),\n    theme,\n  );\n  return (\n    <div\n      ref={ref}\n      aria-id={controlId}\n      onLayout={onLayout}\n      testId={testId}\n      style={mergeStyle(resolved.style, style)}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport const Stack = (props: StackProps): ReactElement =>\n  renderStack(props, useTheme());\n\nexport interface GridProps extends GridSpec, ElementHostProps {\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const renderGrid = (\n  {\n    style,\n    className,\n    children,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    ...spec\n  }: GridProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const resolved = recipeTw.classesToStyle(\n    cn(gridClass(spec), className),\n    theme,\n  );\n  return (\n    <div\n      ref={ref}\n      aria-id={controlId}\n      onLayout={onLayout}\n      testId={testId}\n      style={mergeStyle(resolved.style, style)}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport const Grid = (props: GridProps): ReactElement =>\n  renderGrid(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/Switch.tsx",
      "target": "@ui/gpuixui/primitives/components/Switch.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  createContext,\n  useContext,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport { motion } from \"@gpuix/react\";\nimport type {\n  EventPayload,\n  KeyEvent,\n  MotionTransition,\n  StyleDesc,\n} from \"@gpuix/react\";\nimport * as P from \"effect/Predicate\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { motionTiming } from \"../../motion/timing.js\";\nimport { cn } from \"../../tw/index.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport {\n  decideActivation,\n  isPlainKey,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport { reducedMotionOf, stillTransition } from \"../core/motion.js\";\nimport {\n  switchTargetStyle,\n  switchThumbOffset,\n  switchThumbPaint,\n  switchTrackPaint,\n  type SelectionAccent,\n  type SelectionSpec,\n} from \"../core/selection.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { switchThumbClass, switchTrackClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  checkedChange,\n  composeEvent,\n  composePress,\n  decorative,\n  mergeStyle,\n  suppressSelectionFeedback,\n  useControlState,\n  useFocus,\n  type CheckedChangeProps,\n  type FieldHostProps,\n  type FocusState,\n  type HelpProps,\n  type InteractionProps,\n  type TooltipTriggerProps,\n} from \"./internal.js\";\nimport { selectionValueText } from \"./Checkbox.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\ninterface SwitchPart {\n  readonly spec: SelectionSpec;\n  readonly theme: PrimitiveTheme;\n\n  readonly reducedMotion: boolean;\n}\n\nconst SwitchContext = createContext<SwitchPart | null>(null);\n\nexport interface SwitchThumbProps {\n  readonly children?: ReactNode;\n  readonly style?: StyleDesc;\n  readonly className?: string;\n}\n\nexport interface SwitchProps\n  extends\n    CheckedChangeProps,\n    InteractionProps,\n    TooltipTriggerProps,\n    FieldHostProps,\n    HelpProps {\n  readonly checked?: boolean;\n  readonly defaultChecked?: boolean;\n\n  readonly children?: ReactNode;\n  readonly disabled?: boolean;\n  readonly invalid?: boolean;\n\n  readonly size?: ControlSize;\n\n  readonly accent?: SelectionAccent;\n\n  readonly label?: string;\n\n  readonly reducedMotion?: boolean;\n\n  readonly style?: StateStyle<SwitchState>;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport interface SwitchState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly checked: boolean;\n  readonly invalid: boolean;\n}\n\nconst THUMB_SLIDE: MotionTransition = motionTiming(\"fast\", {\n  phase: \"standard\",\n});\n\nexport const renderSwitchThumb = (\n  props: SwitchThumbProps,\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n  reducedMotion = false,\n): ReactElement => {\n  const thumb = recipeTw.classesToStyle(\n    cn(switchThumbClass(spec), props.className),\n    theme,\n  ).style;\n  const style = mergeStyle(\n    mergeStyle(\n      {\n        ...thumb,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n      },\n\n      switchThumbPaint(spec, theme),\n    ),\n    props.style,\n  );\n  const left = P.isNumber(style.left)\n    ? style.left\n    : switchThumbOffset(spec, theme);\n  return (\n    <motion.div\n      initial={false}\n      animate={{ left }}\n      transition={stillTransition(THUMB_SLIDE, reducedMotion)}\n      style={style}\n    >\n      {props.children}\n    </motion.div>\n  );\n};\n\nexport const SwitchThumb = (props: SwitchThumbProps): ReactElement => {\n  const part = useContext(SwitchContext);\n  if (part === null) {\n    throw new Error(\"SwitchThumb must be used inside Switch\");\n  }\n  return renderSwitchThumb(props, part.spec, part.theme, part.reducedMotion);\n};\n\nexport const renderSwitch = (\n  props: SwitchProps,\n  theme: PrimitiveTheme,\n  state: ValueState<boolean>,\n  focus: FocusState,\n  onValueChange: (next: boolean) => void,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const spec: SelectionSpec = {\n    state: state.current ? \"checked\" : \"unchecked\",\n    size: controlSize(props.size),\n    accent: props.accent ?? \"primary\",\n    invalid: props.invalid,\n    disabled: props.disabled,\n  };\n  const toggle = (): void => {\n    if (props.disabled === true) return;\n    onValueChange(!state.current);\n  };\n  const onKey =\n    (phase: \"down\" | \"up\") =>\n    (event: KeyEvent): void => {\n      if (!isPlainKey(event.modifiers)) return;\n      if (swallowKeyDefault(event.key ?? \"\", phase)) event.preventDefault();\n      if (decideActivation(event.key ?? \"\", phase, event.isHeld === true))\n        toggle();\n    };\n  const resolved = recipeTw.classesToStyle(\n    cn(switchTrackClass(spec), props.className),\n    theme,\n  );\n\n  const painted = mergeStyle(resolved.style, switchTrackPaint(spec, theme));\n  const drawn =\n    spec.disabled === true\n      ? suppressSelectionFeedback(mergeStyle(painted, resolved.disabled))\n      : painted;\n  const track = decorative(\n    mergeStyle(\n      drawn,\n      resolveStyle(props.style, {\n        disabled: spec.disabled === true,\n        focused: focus.focused,\n        checked: spec.state === \"checked\",\n        invalid: spec.invalid === true,\n      }),\n    ),\n  );\n  const target = mergeStyle(\n    switchTargetStyle(spec),\n    controlRing(theme, { disabled: spec.disabled, invalid: spec.invalid }),\n  );\n  return (\n    <div\n      ref={props.ref}\n      role=\"switch\"\n      aria-label={accessibleName(props.label)}\n      aria-description={\n        accessibleName(props.description) ??\n        accessibleName(props[\"aria-description\"])\n      }\n      aria-id={props[\"aria-id\"]}\n      aria-valuetext={selectionValueText(spec.state, messages.selection)}\n      tabIndex={props.disabled === true ? undefined : 0}\n      testId={props.testId}\n      onLayout={props.onLayout}\n      onClick={composePress(props.disabled === true, props.onClick, toggle)}\n      onMouseEnter={props.onMouseEnter}\n      onMouseLeave={props.onMouseLeave}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, onKey(\"down\"))}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, onKey(\"up\"))}\n      onFocus={composeEvent<EventPayload>(props.onFocus, focus.onFocus)}\n      onBlur={composeEvent<EventPayload>(props.onBlur, focus.onBlur)}\n      style={target}\n    >\n      <div style={track}>\n        <SwitchContext.Provider\n          value={{\n            spec,\n            theme,\n            reducedMotion: props.reducedMotion === true,\n          }}\n        >\n          {renderSwitchThumb(\n            { children: props.children },\n            spec,\n            theme,\n            props.reducedMotion === true,\n          )}\n        </SwitchContext.Provider>\n      </div>\n    </div>\n  );\n};\n\nexport const Switch = (props: SwitchProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const focus = useFocus();\n  const state = useControlState(\n    props.checked,\n    props.defaultChecked ?? false,\n    checkedChange(props),\n  );\n\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  return renderSwitch(\n    { ...props, reducedMotion },\n    theme,\n    state.value,\n    focus,\n    state.set,\n    messages,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Text.tsx",
      "target": "@ui/gpuixui/primitives/components/Text.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { cn } from \"../../tw/index.js\";\nimport type { TextAlign, TextTone } from \"../core/style.js\";\nimport type { PrimitiveTheme, TextRole } from \"../core/theme.js\";\nimport { textClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { childrenName, mergeStyle, type ElementHostProps } from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface TextProps extends ElementHostProps {\n  readonly role?: TextRole;\n\n  readonly tone?: TextTone;\n  readonly align?: TextAlign;\n\n  readonly truncate?: boolean;\n\n  readonly clamp?: number;\n\n  readonly selectable?: boolean;\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n  readonly children?: ReactNode;\n}\n\nconst semanticsOf = (\n  role: TextRole,\n): { readonly role?: string; readonly level?: number } =>\n  role === \"h1\"\n    ? { role: \"heading\", level: 1 }\n    : role === \"h2\"\n      ? { role: \"heading\", level: 2 }\n      : role === \"h3\"\n        ? { role: \"heading\", level: 3 }\n        : role === \"code\" || role === \"codeBlock\"\n          ? { role: \"code\" }\n          : {};\n\nexport const renderText = (\n  {\n    role = \"body\",\n    tone = \"default\",\n    align,\n    truncate,\n    clamp,\n    selectable,\n    style,\n    className,\n    testId,\n    ref,\n    \"aria-id\": controlId,\n    onLayout,\n    children,\n  }: TextProps,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const resolved = recipeTw.classesToStyle(\n    cn(\n      textClass({ role, tone, align, truncate, clamp, selectable }),\n      className,\n    ),\n    theme,\n  );\n  const semantics = semanticsOf(role);\n  return (\n    <text\n      ref={ref}\n      testId={testId}\n      role={semantics.role}\n      aria-level={semantics.level}\n      aria-id={controlId}\n      aria-label={\n        semantics.role === undefined ? undefined : childrenName(children)\n      }\n      onLayout={onLayout}\n      style={mergeStyle(resolved.style, style)}\n    >\n      {children}\n    </text>\n  );\n};\n\nexport const Text = (props: TextProps): ReactElement =>\n  renderText(props, useTheme());\n"
    },
    {
      "path": "packages/primitives/src/components/TextArea.tsx",
      "target": "@ui/gpuixui/primitives/components/TextArea.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport * as Option from \"effect/Option\";\nimport { fieldRows } from \"../core/layout.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { controlPaddingOf, controlSize } from \"../core/theme.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { fieldFrame, fieldHostProps, type FieldProps } from \"./Input.js\";\nimport { fieldHint, renderFieldHint } from \"./hint.js\";\nimport {\n  useControlState,\n  useFocus,\n  valueChange,\n  type FocusState,\n  type ValueChangeProps,\n} from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport type TextAreaProps = Omit<FieldProps, \"onSubmit\"> &\n  ValueChangeProps<string> & {\n    readonly minRows?: number;\n\n    readonly maxRows?: number;\n  };\n\nexport const renderTextArea = (\n  props: TextAreaProps,\n  theme: PrimitiveTheme,\n  state: ValueState<string>,\n  focus: FocusState,\n  onValueChange: (value: string) => void,\n): ReactElement => {\n  const host = fieldHostProps(props, theme, state, focus, onValueChange);\n  const vertical = controlPaddingOf(theme, controlSize(props.size));\n  const style: StyleDesc = {\n    ...host.style,\n    height: undefined,\n    paddingTop: vertical,\n    paddingBottom: vertical,\n  };\n  const rows = fieldRows(props.minRows, props.maxRows);\n  return (\n    <textarea\n      {...host}\n      style={style}\n      minRows={rows.minRows}\n      maxRows={rows.maxRows}\n    />\n  );\n};\n\nexport const TextArea = (props: TextAreaProps): ReactElement => {\n  const theme = useTheme();\n  const focus = useFocus();\n  const field = useControlState(\n    props.value,\n    props.defaultValue ?? \"\",\n    valueChange(props),\n  );\n  const hint = fieldHint(props, theme, field.value.current, {\n    size: controlSize(props.size),\n    align: \"top\",\n    origin: \"field\",\n  });\n  return (\n    <div style={fieldFrame}>\n      {renderTextArea(props, theme, field.value, focus, field.set)}\n      {Option.match(hint, {\n        onNone: () => null,\n        onSome: (hint) => renderFieldHint(hint, props.testId),\n      })}\n    </div>\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/ThemeProvider.tsx",
      "target": "@ui/gpuixui/primitives/components/ThemeProvider.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n  ThemeProvider as TokensThemeProvider,\n  useTheme as useTokensTheme,\n  useThemePresence,\n} from \"../../tokens/react.js\";\nimport type { MessagesSource } from \"../core/messages.js\";\nimport type { Density, PrimitiveTheme } from \"../core/index.js\";\nimport { withDensity } from \"./internal.js\";\nimport { MessagesProvider } from \"./MessagesProvider.js\";\n\nexport class MissingThemeProviderError extends Error {\n  constructor() {\n    super(\n      \"useTheme() found no <ThemeProvider>. GPUIxUI primitives never fall back \" +\n        \"to a default palette; wrap the tree in <ThemeProvider theme={theme}> \" +\n        \"from this package or from @gpuixui/tokens/react.\",\n    );\n    this.name = \"MissingThemeProviderError\";\n  }\n}\n\nexport interface ThemeProviderProps {\n  readonly theme: PrimitiveTheme;\n\n  readonly density?: Density | null;\n\n  readonly messages?: MessagesSource | null;\n  readonly children: ReactNode;\n}\n\nexport const ThemeProvider = ({\n  theme,\n  density,\n  messages,\n  children,\n}: ThemeProviderProps): ReactElement => (\n  <TokensThemeProvider theme={withDensity(theme, density)}>\n    <MessagesProvider messages={messages}>{children}</MessagesProvider>\n  </TokensThemeProvider>\n);\n\nexport const useTheme = (): PrimitiveTheme => {\n  const theme = useTokensTheme();\n  if (!useThemePresence()) throw new MissingThemeProviderError();\n  return theme;\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Toggle.tsx",
      "target": "@ui/gpuixui/primitives/components/Toggle.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport type { EventPayload, KeyEvent } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport { reducedMotionOf } from \"../core/motion.js\";\nimport {\n  decideActivation,\n  isPlainKey,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport {\n  busyWord,\n  defaultMessages,\n  pressWord,\n  type PressWords,\n  type PrimitiveMessages,\n} from \"../core/messages.js\";\nimport { controlSize, iconSideOf } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport type { ToggleVariant } from \"../core/variants.js\";\nimport type { ValueState } from \"../core/state.js\";\nimport { toggleClass } from \"../recipe/index.js\";\nimport { toggleSpinnerAccent } from \"../core/variants.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport {\n  childrenName,\n  composeEvent,\n  composePress,\n  mergeStyle,\n  suppressFeedback,\n  useControlState,\n  useFocus,\n  type FieldHostProps,\n  type FocusState,\n  type HelpProps,\n  type InteractionProps,\n  type TooltipTriggerProps,\n} from \"./internal.js\";\nimport { Spinner } from \"./Spinner.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface ToggleProps\n  extends InteractionProps, TooltipTriggerProps, FieldHostProps, HelpProps {\n  readonly pressed?: boolean;\n  readonly defaultPressed?: boolean;\n  readonly onPressedChange?: (pressed: boolean) => void;\n\n  readonly variant?: ToggleVariant;\n\n  readonly size?: ControlSize;\n\n  readonly square?: boolean;\n  readonly disabled?: boolean;\n\n  readonly loading?: boolean;\n\n  readonly loadingLabel?: string;\n\n  readonly reducedMotion?: boolean;\n  readonly leading?: ReactNode;\n  readonly trailing?: ReactNode;\n\n  readonly label?: string;\n\n  readonly style?: StateStyle<ToggleState>;\n\n  readonly className?: string;\n  readonly children?: ReactNode;\n  readonly testId?: string;\n}\n\nexport const pressedValueText = (\n  pressed: boolean,\n  words: PressWords = defaultMessages.press,\n): string => pressWord(pressed, words);\n\nexport interface ToggleState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly selected: boolean;\n}\n\nexport const renderToggle = (\n  props: ToggleProps,\n  theme: PrimitiveTheme,\n  state: ValueState<boolean>,\n  focus: FocusState,\n  onPressedChange: (pressed: boolean) => void,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const { variant = \"default\" } = props;\n  const size = controlSize(props.size);\n  const busy = props.disabled === true || props.loading === true;\n\n  const loadingWord = props.loadingLabel ?? busyWord(messages.loading);\n  const toggle = (): void => {\n    if (busy) return;\n    onPressedChange(!state.current);\n  };\n  const onKey =\n    (phase: \"down\" | \"up\") =>\n    (event: KeyEvent): void => {\n      if (!isPlainKey(event.modifiers)) return;\n      if (swallowKeyDefault(event.key ?? \"\", phase)) event.preventDefault();\n      if (decideActivation(event.key ?? \"\", phase, event.isHeld === true))\n        toggle();\n    };\n  const resolved = recipeTw.classesToStyle(\n    cn(\n      toggleClass(variant, state.current, size, { square: props.square }),\n      props.className,\n    ),\n    theme,\n  );\n  let style = resolved.style;\n  if (props.disabled === true) {\n    style = suppressFeedback(mergeStyle(style, resolved.disabled));\n  }\n  style = mergeStyle(style, controlRing(theme, { disabled: props.disabled }));\n  style = mergeStyle(\n    style,\n    resolveStyle(props.style, {\n      disabled: busy,\n      focused: focus.focused,\n      selected: state.current,\n    }),\n  );\n  return (\n    <div\n      ref={props.ref}\n      role=\"button\"\n      aria-label={accessibleName(props.label) ?? childrenName(props.children)}\n      aria-description={\n        props.loading === true\n          ? accessibleName(loadingWord)\n          : (accessibleName(props.description) ??\n            accessibleName(props[\"aria-description\"]))\n      }\n      aria-id={props[\"aria-id\"]}\n      aria-valuetext={pressedValueText(state.current, messages.press)}\n      tabIndex={props.disabled === true ? undefined : 0}\n      testId={props.testId}\n      onLayout={props.onLayout}\n      onClick={composePress(busy, props.onClick, toggle)}\n      onMouseEnter={props.onMouseEnter}\n      onMouseLeave={props.onMouseLeave}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, onKey(\"down\"))}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, onKey(\"up\"))}\n      onFocus={composeEvent<EventPayload>(props.onFocus, focus.onFocus)}\n      onBlur={composeEvent<EventPayload>(props.onBlur, focus.onBlur)}\n      style={style}\n    >\n      {props.loading === true ? (\n        <Spinner\n          size={iconSideOf(theme, \"sm\")}\n          accent={toggleSpinnerAccent(state.current, props.disabled === true)}\n          label={loadingWord}\n          reducedMotion={props.reducedMotion}\n        />\n      ) : (\n        props.leading\n      )}\n      {props.children}\n      {props.trailing}\n    </div>\n  );\n};\n\nexport const Toggle = (props: ToggleProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const focus = useFocus();\n  const state = useControlState(\n    props.pressed,\n    props.defaultPressed ?? false,\n    props.onPressedChange,\n  );\n\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  return renderToggle(\n    { ...props, reducedMotion },\n    theme,\n    state.value,\n    focus,\n    state.set,\n    messages,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/ToggleGroup.tsx",
      "target": "@ui/gpuixui/primitives/components/ToggleGroup.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement, ReactNode } from \"react\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport type { EventPayload, KeyEvent } from \"@gpuix/react\";\nimport { resolveStyle, type StateStyle } from \"@gpuix/react/floating\";\nimport { cn } from \"../../tw/index.js\";\nimport { accessibleName } from \"../core/a11y.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport {\n  decideAbsolute,\n  decideActivation,\n  decideMove,\n  isPlainKey,\n  orientationOf,\n  swallowKeyDefault,\n} from \"../core/keyboard.js\";\nimport { rovingIndex } from \"../core/roving.js\";\nimport { defaultMessages, type PrimitiveMessages } from \"../core/messages.js\";\nimport { toggleSelection } from \"../core/selection.js\";\nimport { controlSize } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport type { ToggleVariant } from \"../core/variants.js\";\nimport { stackClass, toggleClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { pressedValueText } from \"./Toggle.js\";\nimport {\n  focusedOption,\n  groupOptions,\n  optionBody,\n  optionDisabled,\n  optionHelp,\n  optionItems,\n  optionName,\n  useRovingFocus,\n  useTypeAhead,\n  type GroupFocus,\n  type GroupHandlers,\n  type GroupItemProps,\n  type GroupKeys,\n  type GroupOption,\n} from \"./group.js\";\nimport {\n  composeEvent,\n  composePress,\n  mergeRefs,\n  mergeStyle,\n  suppressFeedback,\n  useControlState,\n  type FieldHostProps,\n  type HelpProps,\n  type InteractionProps,\n} from \"./internal.js\";\nimport { useMessages } from \"./MessagesProvider.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport const TOGGLE_GROUP_MODES = [\"single\", \"multiple\"] as const;\nexport type ToggleGroupMode = (typeof TOGGLE_GROUP_MODES)[number];\n\nexport type ToggleGroupOption = GroupOption;\n\nexport type ToggleGroupItemProps = GroupItemProps;\n\nexport const ToggleGroupItem = (_props: ToggleGroupItemProps): null => null;\n\nexport const toggleGroupItems = (props: {\n  readonly options?: ReadonlyArray<ToggleGroupOption>;\n  readonly children?: ReactNode;\n}): ReadonlyArray<ToggleGroupOption> =>\n  props.options === undefined || props.options === null\n    ? optionItems(props.children, ToggleGroupItem)\n    : groupOptions(props.options);\n\nexport const toggleGroupValues = (value: unknown): ReadonlyArray<string> =>\n  P.isString(value)\n    ? value === \"\"\n      ? []\n      : [value]\n    : Arr.isArray(value)\n      ? Arr.filter(value, P.isString)\n      : [];\n\nconst storedSelection = (\n  value: unknown,\n): Option.Option<ReadonlyArray<string>> =>\n  P.isString(value) || Arr.isArray(value)\n    ? Option.some(toggleGroupValues(value))\n    : Option.none();\n\nexport function toggleGroupValue(\n  mode: \"single\",\n  values: ReadonlyArray<string>,\n): string;\nexport function toggleGroupValue(\n  mode: \"multiple\",\n  values: ReadonlyArray<string>,\n): ReadonlyArray<string>;\nexport function toggleGroupValue(\n  mode: ToggleGroupMode,\n  values: ReadonlyArray<string>,\n): string | ReadonlyArray<string> {\n  return mode === \"single\" ? (values[0] ?? \"\") : values;\n}\n\ninterface ToggleGroupBase extends InteractionProps, FieldHostProps, HelpProps {\n  readonly options?: ReadonlyArray<ToggleGroupOption>;\n\n  readonly children?: ReactNode;\n\n  readonly orientation?: \"vertical\" | \"horizontal\";\n\n  readonly variant?: ToggleVariant;\n\n  readonly size?: ControlSize;\n\n  readonly square?: boolean;\n\n  readonly disabled?: boolean;\n\n  readonly label?: string;\n\n  readonly style?: StateStyle<ToggleGroupState>;\n\n  readonly itemStyle?: StateStyle<ToggleItemState>;\n\n  readonly className?: string;\n\n  readonly testId?: string;\n\n  readonly itemTestId?: (value: string) => string;\n}\n\nexport interface SingleToggleGroupProps extends ToggleGroupBase {\n  readonly type: \"single\";\n\n  readonly value?: string;\n  readonly defaultValue?: string;\n  readonly onValueChange?: (next: string) => void;\n}\n\nexport interface MultipleToggleGroupProps extends ToggleGroupBase {\n  readonly type?: \"multiple\";\n\n  readonly value?: ReadonlyArray<string>;\n  readonly defaultValue?: ReadonlyArray<string>;\n  readonly onValueChange?: (next: ReadonlyArray<string>) => void;\n}\n\nexport type ToggleGroupProps =\n  | SingleToggleGroupProps\n  | MultipleToggleGroupProps;\n\nexport interface ToggleGroupState {\n  readonly disabled: boolean;\n\n  readonly focused: boolean;\n}\n\nexport interface ToggleItemState {\n  readonly disabled: boolean;\n  readonly focused: boolean;\n  readonly pressed: boolean;\n}\n\nexport interface ToggleGroupHandlers extends GroupHandlers, GroupKeys {\n  readonly onToggle: (value: string) => void;\n}\n\nexport const renderToggleGroup = (\n  props: ToggleGroupProps,\n  theme: PrimitiveTheme,\n  values: ReadonlyArray<string>,\n  preferredIndex: Option.Option<number>,\n  focus: GroupFocus,\n  handlers: ToggleGroupHandlers,\n  messages: PrimitiveMessages = defaultMessages,\n): ReactElement => {\n  const { variant = \"default\", square = false } = props;\n\n  const orientation = orientationOf(props.orientation, \"horizontal\");\n  const size = controlSize(props.size);\n  const options = toggleGroupItems(props);\n  const count = Arr.length(options);\n  const isDisabled = optionDisabled(options, props.disabled);\n\n  const focused = focusedOption(count, focus.focusedIndex);\n\n  const activeIndex = rovingIndex(count, preferredIndex, isDisabled);\n  const toggle = (index: number): void => {\n    const option = options[index];\n    if (option === undefined || isDisabled(index)) return;\n    handlers.onToggle(option.value);\n  };\n  const focusMove = (index: number): true => {\n    handlers.onFocusMove(index);\n    return true;\n  };\n  const move = (key: string): boolean =>\n    Option.match(activeIndex, {\n      onNone: () => false,\n      onSome: (index) =>\n        Option.match(decideMove(index, count, key, orientation, isDisabled), {\n          onNone: () => false,\n          onSome: focusMove,\n        }),\n    });\n  const jump = (key: string): boolean =>\n    Option.match(decideAbsolute(count, key, isDisabled), {\n      onNone: () => false,\n      onSome: focusMove,\n    });\n  const activate = (): void => {\n    Option.match(activeIndex, { onNone: () => {}, onSome: toggle });\n  };\n  const onKeyDown = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    const key = event.key ?? \"\";\n    if (swallowKeyDefault(key, \"down\")) event.preventDefault();\n    if (move(key) || jump(key)) return;\n\n    const typed = Option.flatMap(\n      Option.all([Option.fromNullishOr(handlers.typeAhead), activeIndex]),\n      ([match, from]) => match(from, key),\n    );\n    if (Option.match(typed, { onNone: () => false, onSome: focusMove })) return;\n    if (decideActivation(key, \"down\", event.isHeld === true)) activate();\n  };\n\n  const onKeyUp = (event: KeyEvent): void => {\n    if (!isPlainKey(event.modifiers)) return;\n    if (decideActivation(event.key ?? \"\", \"up\", false)) activate();\n  };\n  const group = mergeStyle(\n    recipeTw.classesToStyle(\n      cn(\n        stackClass({\n          direction: orientation === \"horizontal\" ? \"row\" : \"column\",\n          gap: \"xs\",\n        }),\n        props.className,\n      ),\n      theme,\n    ).style,\n    resolveStyle(props.style, {\n      disabled: props.disabled === true,\n      focused: Option.isSome(focused),\n    }),\n  );\n  return (\n    <div\n      role=\"group\"\n      aria-label={accessibleName(props.label)}\n      aria-description={accessibleName(props.description)}\n      testId={props.testId}\n      onKeyDown={composeEvent<KeyEvent>(props.onKeyDown, onKeyDown)}\n      onKeyUp={composeEvent<KeyEvent>(props.onKeyUp, onKeyUp)}\n      style={group}\n    >\n      {Arr.map(options, (option, index) => {\n        const disabled = isDisabled(index);\n        const pressed = Arr.contains(values, option.value);\n        const resolved = recipeTw.classesToStyle(\n          toggleClass(variant, pressed, size, { square }),\n          theme,\n        );\n        let style = resolved.style;\n        if (disabled) {\n          style = suppressFeedback(mergeStyle(style, resolved.disabled));\n        }\n        style = mergeStyle(style, controlRing(theme, { disabled }));\n        style = mergeStyle(\n          style,\n          resolveStyle(props.itemStyle, {\n            disabled,\n            focused: Option.contains(focused, index),\n            pressed,\n          }),\n        );\n\n        const addressed = Option.contains(activeIndex, index);\n        const register = handlers.registerItem(index);\n        return (\n          <div\n            key={option.value}\n            ref={addressed ? mergeRefs(register, props.ref) : register}\n            role=\"button\"\n            aria-label={optionName(option)}\n            aria-description={optionHelp(option)}\n            aria-id={addressed ? props[\"aria-id\"] : undefined}\n            aria-valuetext={pressedValueText(pressed, messages.press)}\n            testId={props.itemTestId?.(option.value)}\n            tabIndex={\n              disabled\n                ? undefined\n                : Option.contains(activeIndex, index)\n                  ? 0\n                  : -1\n            }\n            onLayout={addressed ? props.onLayout : undefined}\n            onClick={composePress(disabled, props.onClick, () => {\n              toggle(index);\n            })}\n            onFocus={composeEvent<EventPayload>(props.onFocus, () =>\n              focus.onFocus(index),\n            )}\n            onBlur={composeEvent<EventPayload>(props.onBlur, () =>\n              focus.onBlur(index),\n            )}\n            style={style}\n          >\n            {optionBody(option, disabled, theme)}\n          </div>\n        );\n      })}\n    </div>\n  );\n};\n\nexport const ToggleGroup = (props: ToggleGroupProps): ReactElement => {\n  const theme = useTheme();\n  const messages = useMessages();\n  const focus = useRovingFocus();\n  const mode = props.type ?? \"multiple\";\n\n  const notify = (next: ReadonlyArray<string>): void => {\n    if (props.type === \"single\")\n      props.onValueChange?.(toggleGroupValue(\"single\", next));\n    else props.onValueChange?.(toggleGroupValue(\"multiple\", next));\n  };\n  const selected = useControlState<ReadonlyArray<string>>(\n    Option.getOrUndefined(storedSelection(props.value)),\n    Option.getOrElse(storedSelection(props.defaultValue), () => []),\n    notify,\n  );\n  const options = toggleGroupItems(props);\n  const typeAhead = useTypeAhead(\n    options,\n    optionDisabled(options, props.disabled),\n  );\n  const preferred: Option.Option<number> = Option.firstSomeOf([\n    focus.focusedIndex,\n    Arr.findFirstIndex(options, (option) =>\n      Arr.contains(selected.value.current, option.value),\n    ),\n  ]);\n  return renderToggleGroup(\n    props,\n    theme,\n    selected.value.current,\n    preferred,\n    focus,\n    {\n      onToggle: (value) =>\n        selected.set(toggleSelection(mode, selected.value.current, value)),\n      onFocusMove: focus.onFocusMove,\n      registerItem: focus.registerItem,\n      typeAhead,\n    },\n    messages,\n  );\n};\n"
    },
    {
      "path": "packages/primitives/src/components/Tooltip.tsx",
      "target": "@ui/gpuixui/primitives/components/Tooltip.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  Fragment,\n  createContext,\n  cloneElement,\n  isValidElement,\n  useEffect,\n  useContext,\n  useLayoutEffect,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport type { MotionProps, StyleDesc } from \"@gpuix/react\";\nimport type { FloatingAlign, FloatingSide } from \"@gpuix/react/floating\";\nimport { useMotionPreference } from \"../../motion/react/presence.js\";\nimport { cn } from \"../../tw/index.js\";\nimport {\n  Tooltip as GpuixTooltip,\n  TooltipContent,\n  TooltipProvider as GpuixTooltipProvider,\n  TooltipTrigger,\n} from \"@gpuix/react/tooltip\";\nimport { accessibleName, visibleCopy } from \"../core/a11y.js\";\nimport { reducedMotionOf } from \"../core/motion.js\";\nimport {\n  exitWindowMs,\n  fadeFromAnchor,\n  presenceStyle,\n  surfaceMotion,\n  surfaceTiming,\n  type AnchorSide,\n  type SurfaceTiming,\n} from \"../core/presence.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { tooltipClass } from \"../recipe/index.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle } from \"./internal.js\";\nimport { useTheme } from \"./ThemeProvider.js\";\n\nexport interface TooltipProviderProps {\n  readonly delay?: number;\n\n  readonly skipDelay?: number;\n\n  readonly disableHoverableContent?: boolean;\n  readonly children: ReactNode;\n}\n\nexport const renderTooltipProvider = ({\n  delay,\n  skipDelay,\n  disableHoverableContent,\n  children,\n}: TooltipProviderProps): ReactElement => (\n  <GpuixTooltipProvider\n    delayDuration={delay}\n    skipDelayDuration={skipDelay}\n    disableHoverableContent={disableHoverableContent}\n  >\n    {children}\n  </GpuixTooltipProvider>\n);\n\nexport const APPKIT_TOOLTIP_DELAY_MS = 1000;\n\nexport const tooltipDelayFor = (theme: PrimitiveTheme): number | undefined =>\n  theme.interaction === \"appkit\" ? APPKIT_TOOLTIP_DELAY_MS : undefined;\n\nconst ProvidedTiming = createContext(false);\n\nexport const TooltipProvider = (props: TooltipProviderProps): ReactElement => {\n  const theme = useTheme();\n  return (\n    <ProvidedTiming.Provider value>\n      {renderTooltipProvider({\n        ...props,\n        delay: props.delay ?? tooltipDelayFor(theme),\n      })}\n    </ProvidedTiming.Provider>\n  );\n};\n\nexport interface TooltipProps {\n  readonly content: ReactNode;\n\n  readonly description?: string;\n\n  readonly children: ReactNode;\n\n  readonly side?: FloatingSide;\n\n  readonly align?: FloatingAlign;\n\n  readonly delay?: number;\n\n  readonly open?: boolean;\n\n  readonly defaultOpen?: boolean;\n\n  readonly onOpenChange?: (open: boolean) => void;\n\n  readonly disableHoverableContent?: boolean;\n\n  readonly reducedMotion?: boolean;\n\n  readonly style?: StyleDesc;\n\n  readonly className?: string;\n  readonly testId?: string;\n}\n\nexport const tooltipStyle = (\n  theme: PrimitiveTheme,\n  className?: string,\n  caller?: StyleDesc,\n): StyleDesc =>\n  mergeStyle(\n    recipeTw.classesToStyle(cn(tooltipClass, className), theme).style,\n    caller,\n  );\n\nconst describedTrigger = (\n  children: ReactNode,\n  text: string | undefined,\n  explicit: boolean,\n): ReactNode => {\n  const name = accessibleName(text);\n  if (\n    name === undefined ||\n    !isValidElement<Record<string, unknown>>(children) ||\n    children.type === Fragment\n  ) {\n    return children;\n  }\n  const own = children.props[\"aria-description\"];\n  const ownName = typeof own === \"string\" ? accessibleName(own) : undefined;\n  return explicit || ownName === undefined\n    ? cloneElement(children, { \"aria-description\": name })\n    : children;\n};\n\nexport interface TooltipView {\n  readonly mounted: boolean;\n\n  readonly present: boolean;\n\n  readonly side: AnchorSide;\n  readonly motion: MotionProps;\n\n  readonly onOpenChange: (open: boolean) => void;\n}\n\nconst tipCopy = (content: ReactNode): ReactNode =>\n  content === null || content === undefined\n    ? null\n    : typeof content === \"string\"\n      ? (visibleCopy(content) ?? null)\n      : content;\n\nexport const renderTooltip = (\n  {\n    content,\n    description,\n    children,\n    side,\n    align = \"center\",\n    delay,\n    open,\n    defaultOpen,\n    onOpenChange,\n    disableHoverableContent,\n    style,\n    className,\n    testId,\n  }: TooltipProps,\n  theme: PrimitiveTheme,\n  view?: TooltipView,\n): ReactElement => {\n  const copy = tipCopy(content);\n  return (\n    <GpuixTooltip\n      delayDuration={delay}\n      open={view === undefined ? open : view.mounted}\n      defaultOpen={defaultOpen}\n      onOpenChange={view === undefined ? onOpenChange : view.onOpenChange}\n      disableHoverableContent={disableHoverableContent}\n    >\n      <TooltipTrigger asChild>\n        {describedTrigger(\n          children,\n          description ?? (typeof content === \"string\" ? content : undefined),\n          description !== undefined,\n        )}\n      </TooltipTrigger>\n      {copy === null ? null : (\n        <TooltipContent\n          side={view === undefined ? side : view.side}\n          align={align}\n          motion={view?.motion}\n          style={mergeStyle(\n            tooltipStyle(theme, className, style),\n            view === undefined ? undefined : presenceStyle(view.present),\n          )}\n          testId={testId}\n        >\n          {copy}\n        </TooltipContent>\n      )}\n    </GpuixTooltip>\n  );\n};\n\nconst TOOLTIP_OFFSET = 2;\n\nconst useHeldOpen = (wanted: boolean, timing: SurfaceTiming): boolean => {\n  const [mounted, setMounted] = useState(wanted);\n  const windowMs = exitWindowMs(timing);\n\n  useLayoutEffect(() => {\n    if (wanted) setMounted(true);\n  }, [wanted]);\n\n  useEffect(() => {\n    if (wanted || !mounted) return;\n    if (windowMs === 0) {\n      setMounted(false);\n      return;\n    }\n    const handle = setTimeout(() => setMounted(false), windowMs);\n    return () => clearTimeout(handle);\n  }, [mounted, wanted, windowMs]);\n\n  return mounted;\n};\n\nexport const Tooltip = (props: TooltipProps): ReactElement => {\n  const theme = useTheme();\n\n  const reducedMotion = reducedMotionOf(\n    props.reducedMotion,\n    useMotionPreference(),\n  );\n  const timing = surfaceTiming(reducedMotion);\n  const [requested, setRequested] = useState(props.defaultOpen === true);\n  const wanted = props.open ?? requested;\n  const mounted = useHeldOpen(wanted, timing);\n  const requestOpen = (next: boolean): void => {\n    props.onOpenChange?.(next);\n    if (props.open === undefined) setRequested(next);\n  };\n  const side = props.side ?? \"top\";\n\n  const provided = useContext(ProvidedTiming);\n  const delay = props.delay ?? (provided ? undefined : tooltipDelayFor(theme));\n  return renderTooltip({ ...props, delay }, theme, {\n    mounted,\n    present: wanted,\n    side,\n    motion: surfaceMotion(fadeFromAnchor(side, TOOLTIP_OFFSET), wanted, timing),\n    onOpenChange: requestOpen,\n  });\n};\n"
    },
    {
      "path": "packages/primitives/src/components/group.tsx",
      "target": "@ui/gpuixui/primitives/components/group.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport {\n  Children,\n  Fragment,\n  isValidElement,\n  useEffect,\n  useRef,\n  useState,\n  type ComponentType,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport { useGpuix } from \"@gpuix/react\";\nimport type { PublicInstance, StyleDesc } from \"@gpuix/react\";\nimport { targetSize } from \"../../tokens/index.js\";\nimport { accessibleName, visibleCopy } from \"../core/a11y.js\";\nimport { controlRing } from \"../core/focus.js\";\nimport { storedList, storedText } from \"../core/stored.js\";\nimport {\n  decideTypeAhead,\n  isTypeAheadKey,\n  TYPE_AHEAD_RESET_MS,\n} from \"../core/keyboard.js\";\nimport type { PrimitiveTheme } from \"../core/theme.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle } from \"./internal.js\";\nimport { Text } from \"./Text.js\";\n\nexport interface GroupOption {\n  readonly value: string;\n  readonly label: string;\n  readonly description?: string;\n  readonly disabled?: boolean;\n\n  readonly content?: ReactNode;\n}\n\nexport interface GroupItemProps {\n  readonly value: string;\n\n  readonly label: string;\n  readonly description?: string;\n  readonly disabled?: boolean;\n\n  readonly children?: ReactNode;\n}\n\nexport const optionItems = <Props extends GroupItemProps>(\n  children: ReactNode,\n  Item: ComponentType<Props>,\n): ReadonlyArray<GroupOption> =>\n  Arr.flatMap(Children.toArray(children), (child) => {\n    if (!isValidElement<Props>(child)) return [];\n    if (child.type === Fragment) return optionItems(child.props.children, Item);\n    if (child.type !== Item) return [];\n    return [\n      {\n        value: child.props.value,\n        label: child.props.label,\n        description: child.props.description,\n        disabled: child.props.disabled,\n        content: child.props.children,\n      },\n    ];\n  });\n\nconst storedContent = (value: unknown): ReactNode | undefined =>\n  isValidElement(value) || P.isString(value) || P.isNumber(value)\n    ? value\n    : undefined;\n\nconst storedOption = (entry: unknown): Option.Option<GroupOption> => {\n  if (!P.isObject(entry)) return Option.none();\n  const value = storedText(entry[\"value\"]);\n  if (value === undefined) return Option.none();\n  const description = storedText(entry[\"description\"]);\n  const content = storedContent(entry[\"content\"]);\n  return Option.some({\n    value,\n\n    label: storedText(entry[\"label\"]) ?? \"\",\n    disabled: entry[\"disabled\"] === true,\n    ...(description === undefined ? {} : { description }),\n    ...(content === undefined ? {} : { content }),\n  });\n};\n\nexport const groupOptions = (value: unknown): ReadonlyArray<GroupOption> =>\n  storedList(value, storedOption);\n\nexport const optionName = (option: GroupOption): string | undefined =>\n  accessibleName(option.label);\n\nexport const optionHelp = (option: GroupOption): string | undefined =>\n  accessibleName(option.description);\n\nexport const optionBody = (\n  option: GroupOption,\n  disabled: boolean,\n  theme: PrimitiveTheme,\n): ReactElement => {\n  const caption = visibleCopy(option.description);\n  return (\n    <div\n      style={\n        recipeTw.classesToStyle(\"flex flex-row items-center gap-sm\", theme)\n          .style\n      }\n    >\n      {option.content === undefined ? (\n        <>\n          <Text role=\"label\" tone={disabled ? \"muted\" : \"default\"}>\n            {option.label}\n          </Text>\n          {caption === undefined ? null : (\n            <Text role=\"caption\" tone=\"muted\">\n              {caption}\n            </Text>\n          )}\n        </>\n      ) : (\n        option.content\n      )}\n    </div>\n  );\n};\n\nexport const optionRowClass = (disabled: boolean): string =>\n  `flex flex-row items-center gap-sm ${disabled ? \"cursor-not-allowed\" : \"cursor-pointer\"}`;\n\nexport const optionRowStyle = (\n  state: { readonly disabled: boolean; readonly invalid?: boolean },\n  theme: PrimitiveTheme,\n): StyleDesc =>\n  mergeStyle(\n    mergeStyle(\n      recipeTw.classesToStyle(optionRowClass(state.disabled), theme).style,\n      controlRing(theme, {\n        disabled: state.disabled,\n        invalid: state.invalid,\n      }),\n    ),\n    { minHeight: targetSize.minimum },\n  );\n\nexport interface GroupFocus {\n  readonly focusedIndex: Option.Option<number>;\n  readonly onFocus: (index: number) => void;\n\n  readonly onBlur: (index: number) => void;\n}\n\nexport interface GroupHandlers {\n  readonly onFocusMove: (index: number) => void;\n\n  readonly registerItem: (\n    index: number,\n  ) => (instance: PublicInstance | null) => void;\n}\n\nexport type TypeAhead = (from: number, key: string) => Option.Option<number>;\n\nexport interface GroupKeys {\n  readonly typeAhead?: TypeAhead;\n}\n\nexport const optionDisabled =\n  (\n    options: ReadonlyArray<GroupOption>,\n    groupDisabled: boolean | undefined,\n  ): ((index: number) => boolean) =>\n  (index) =>\n    groupDisabled === true || options[index]?.disabled === true;\n\nexport const focusedOption = (\n  count: number,\n  focused: Option.Option<number>,\n): Option.Option<number> =>\n  Option.filter(focused, (index) => index >= 0 && index < count);\n\nexport const useTypeAhead = (\n  options: ReadonlyArray<GroupOption>,\n  isDisabled: (index: number) => boolean,\n): TypeAhead => {\n  const buffer = useRef(\"\");\n\n  const reset = useRef<Option.Option<ReturnType<typeof setTimeout>>>(\n    Option.none(),\n  );\n  const cancel = (): void =>\n    Option.match(reset.current, {\n      onNone: () => {},\n      onSome: clearTimeout,\n    });\n  useEffect(\n    () => () => {\n      cancel();\n    },\n    [],\n  );\n  return (from, key) => {\n    if (!isTypeAheadKey(key)) return Option.none();\n    buffer.current = `${buffer.current}${key}`;\n    cancel();\n    reset.current = Option.some(\n      setTimeout(() => {\n        buffer.current = \"\";\n      }, TYPE_AHEAD_RESET_MS),\n    );\n    return decideTypeAhead(\n      Arr.map(options, (option) => option.label),\n      from,\n      buffer.current,\n      isDisabled,\n    );\n  };\n};\n\nexport interface RovingFocus extends GroupFocus, GroupHandlers {}\n\nexport const useRovingFocus = (): RovingFocus => {\n  const { renderer } = useGpuix();\n  const [focusedIndex, setFocusedIndex] = useState<Option.Option<number>>(\n    Option.none(),\n  );\n  const ids = useRef<Map<number, number>>(new Map());\n  return {\n    focusedIndex,\n    onFocus: (index) => setFocusedIndex(Option.some(index)),\n    onBlur: (index) =>\n      setFocusedIndex((current) =>\n        Option.filter(current, (focused) => focused !== index),\n      ),\n    registerItem: (index) => (instance) => {\n      if (instance === null) ids.current.delete(index);\n      else ids.current.set(index, instance.id);\n    },\n    onFocusMove: (index) =>\n      Option.match(\n        Option.all({\n          id: Option.fromNullishOr(ids.current.get(index)),\n          target: Option.fromNullishOr(renderer),\n        }),\n        {\n          onNone: () => {},\n\n          onSome: ({ id, target }) => {\n            target.focusElement?.(id);\n            setFocusedIndex(Option.some(index));\n          },\n        },\n      ),\n  };\n};\n"
    },
    {
      "path": "packages/primitives/src/components/hint.tsx",
      "target": "@ui/gpuixui/primitives/components/hint.tsx",
      "type": "registry:component",
      "content": "/** @jsxImportSource @gpuix/react */\nimport type { ReactElement } from \"react\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { disabledOpacity } from \"../../tokens/index.js\";\nimport { pipe } from \"effect\";\nimport * as Option from \"effect/Option\";\nimport { visibleCopy } from \"../core/a11y.js\";\nimport { borderWidthOf, colorRole, controlPaddingOf } from \"../core/theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"../core/theme.js\";\nimport { recipeTw } from \"../recipe/registry.js\";\nimport { mergeStyle } from \"./internal.js\";\n\nexport type FieldHintAlign = \"center\" | \"top\";\n\nexport interface FieldHint {\n  readonly text: string;\n  readonly frame: StyleDesc;\n  readonly textStyle: StyleDesc;\n}\n\nexport interface HintField {\n  readonly placeholder?: string;\n  readonly disabled?: boolean;\n}\n\nexport type HintSpec =\n  | {\n      readonly origin: \"field\" | \"content\";\n      readonly size: ControlSize;\n      readonly align: FieldHintAlign;\n    }\n  | {\n      readonly origin: \"host\";\n      readonly align: FieldHintAlign;\n      readonly style?: StyleDesc | undefined;\n    };\n\ninterface HintInsets {\n  readonly border: number;\n  readonly paddingLeft: number;\n  readonly paddingRight: number;\n  readonly paddingTop: number;\n}\n\nconst pixels = (value: number | undefined): number => value ?? 0;\n\nconst hintInsets = (theme: PrimitiveTheme, spec: HintSpec): HintInsets => {\n  if (spec.origin === \"host\") {\n    const style = spec.style ?? {};\n    return {\n      border: pixels(style.borderWidth),\n      paddingLeft: pixels(style.paddingLeft),\n      paddingRight: pixels(style.paddingRight),\n      paddingTop: pixels(style.paddingTop),\n    };\n  }\n  const field = spec.origin === \"field\";\n  const padding = field ? controlPaddingOf(theme, spec.size) : 0;\n  return {\n    border: field ? borderWidthOf(theme, \"thin\") : 0,\n    paddingLeft: padding,\n    paddingRight: padding,\n    paddingTop: padding,\n  };\n};\n\nexport const hintFrame = (theme: PrimitiveTheme, spec: HintSpec): StyleDesc => {\n  const insets = hintInsets(theme, spec);\n  return {\n    position: \"absolute\",\n    top: 0,\n    right: 0,\n    bottom: 0,\n    left: 0,\n    display: \"flex\",\n    flexDirection: \"row\",\n    alignItems: spec.align === \"center\" ? \"center\" : \"flex-start\",\n    paddingLeft: insets.border + insets.paddingLeft,\n    paddingRight: insets.paddingRight,\n    paddingTop: spec.align === \"top\" ? insets.border + insets.paddingTop : 0,\n    overflow: \"hidden\",\n    pointerEvents: \"none\",\n  };\n};\n\nconst hintType = (theme: PrimitiveTheme, spec: HintSpec): StyleDesc =>\n  spec.origin === \"host\"\n    ? {\n        fontSize: spec.style?.fontSize,\n        lineHeight: spec.style?.lineHeight,\n        fontFamily: spec.style?.fontFamily,\n        textAlign: spec.style?.textAlign,\n      }\n    : recipeTw.classesToStyle(\"type-body font-sans\", theme).style;\n\nexport const fieldHint = (\n  props: HintField,\n  theme: PrimitiveTheme,\n  value: string,\n  spec: HintSpec,\n): Option.Option<FieldHint> =>\n  pipe(\n    Option.fromNullishOr(visibleCopy(props.placeholder)),\n    Option.filter(() => value.length === 0),\n    Option.map((text) => ({\n      text,\n      frame: hintFrame(theme, spec),\n      textStyle: mergeStyle(\n        mergeStyle(\n          recipeTw.classesToStyle(\"whitespace-nowrap text-ellipsis\", theme)\n            .style,\n          hintType(theme, spec),\n        ),\n        props.disabled === true\n          ? { color: colorRole(theme, \"placeholder\"), opacity: disabledOpacity }\n          : { color: colorRole(theme, \"placeholder\") },\n      ),\n    })),\n  );\n\nexport const renderFieldHint = (\n  hint: FieldHint,\n  testId: string | undefined,\n): ReactElement => (\n  <div style={hint.frame}>\n    <text\n      testId={testId === undefined ? undefined : `${testId}-hint`}\n      style={hint.textStyle}\n    >\n      {hint.text}\n    </text>\n  </div>\n);\n\nexport { fieldHelp } from \"../core/a11y.js\";\n\nexport const hintHost = (layout: StyleDesc = {}): StyleDesc => ({\n  position: \"relative\",\n  display: \"flex\",\n  ...layout,\n});\n"
    },
    {
      "path": "packages/primitives/src/components/index.ts",
      "target": "@ui/gpuixui/primitives/components/index.ts",
      "type": "registry:lib",
      "content": "/** The public component surface of `@gpuixui/primitives`. */\n\nexport {\n  MissingThemeProviderError,\n  ThemeProvider,\n  useTheme,\n} from \"./ThemeProvider.js\";\nexport type { ThemeProviderProps } from \"./ThemeProvider.js\";\n\nexport { MessagesProvider, useMessages } from \"./MessagesProvider.js\";\nexport type { MessagesProviderProps } from \"./MessagesProvider.js\";\n\nexport type {\n  ElementHostProps,\n  InteractionProps,\n  TooltipTriggerProps,\n} from \"./internal.js\";\n\nexport { Box } from \"./Box.js\";\nexport type { BoxProps } from \"./Box.js\";\n\nexport { Grid, Stack } from \"./Stack.js\";\nexport type { GridProps, StackProps } from \"./Stack.js\";\n\nexport { Separator, Spacer } from \"./Separator.js\";\nexport type { SeparatorProps, SpacerProps } from \"./Separator.js\";\n\nexport { ScrollArea, ScrollAreaViewport } from \"./ScrollArea.js\";\nexport type { ScrollAreaProps, ScrollAreaViewportProps } from \"./ScrollArea.js\";\n\nexport { Text } from \"./Text.js\";\nexport type { TextProps } from \"./Text.js\";\n\nexport { Label, labelledBy } from \"./Label.js\";\nexport type { LabelProps } from \"./Label.js\";\n\nexport { Button, ToolbarScope } from \"./Button.js\";\nexport type { ButtonFocus, ButtonProps, ButtonState } from \"./Button.js\";\n\nexport { IconButton, iconButtonProps } from \"./IconButton.js\";\nexport type { IconButtonProps } from \"./IconButton.js\";\n\nexport {\n  ButtonGroup,\n  BUTTON_GROUP_MODES,\n  BUTTON_GROUP_ORIENTATIONS,\n  buttonGroupModeOf,\n  buttonGroupOrientationOf,\n  isButtonGroupMode,\n  isButtonGroupOrientation,\n} from \"./ButtonGroup.js\";\nexport type {\n  ButtonGroupMode,\n  ButtonGroupOrientation,\n  ButtonGroupProps,\n} from \"./ButtonGroup.js\";\n\nexport { pressedValueText, Toggle } from \"./Toggle.js\";\nexport type { ToggleProps, ToggleState } from \"./Toggle.js\";\n\nexport {\n  TOGGLE_GROUP_MODES,\n  ToggleGroup,\n  ToggleGroupItem,\n  toggleGroupItems,\n  toggleGroupValue,\n  toggleGroupValues,\n} from \"./ToggleGroup.js\";\nexport type {\n  ToggleGroupItemProps,\n  ToggleGroupMode,\n  ToggleGroupOption,\n  ToggleGroupProps,\n  ToggleGroupState,\n  ToggleItemState,\n} from \"./ToggleGroup.js\";\n\nexport { Input } from \"./Input.js\";\nexport type { FieldProps, FieldState, InputProps } from \"./Input.js\";\n\nexport { fieldHint, hintFrame, hintHost, renderFieldHint } from \"./hint.js\";\nexport type { FieldHint, FieldHintAlign, HintField, HintSpec } from \"./hint.js\";\n\nexport {\n  NumberField,\n  NumberFieldDecrement,\n  NumberFieldIncrement,\n  NumberFieldInput,\n  NumberFieldScrubHandle,\n  numberRange,\n} from \"./NumberField.js\";\nexport type {\n  NumberFieldInputProps,\n  NumberFieldProps,\n  NumberFieldScrubHandleProps,\n  NumberFieldStepperProps,\n} from \"./NumberField.js\";\n\nexport { OTPField, OTPFieldInput, otpCellLabel } from \"./OTPField.js\";\nexport type { OTPFieldInputProps, OTPFieldProps } from \"./OTPField.js\";\n\nexport { TextArea } from \"./TextArea.js\";\nexport type { TextAreaProps } from \"./TextArea.js\";\n\nexport {\n  Checkbox,\n  CheckboxIndicator,\n  checkboxState,\n  selectionValueText,\n} from \"./Checkbox.js\";\nexport type {\n  CheckboxIndicatorProps,\n  CheckboxProps,\n  CheckboxState,\n} from \"./Checkbox.js\";\n\nexport {\n  CheckboxGroup,\n  CheckboxGroupItem,\n  checkboxGroupItems,\n} from \"./CheckboxGroup.js\";\nexport type {\n  CheckboxGroupItemProps,\n  CheckboxGroupOption,\n  CheckboxGroupProps,\n  CheckboxGroupState,\n  CheckboxItemState,\n} from \"./CheckboxGroup.js\";\n\nexport type { GroupItemProps, GroupOption, TypeAhead } from \"./group.js\";\nexport { useTypeAhead } from \"./group.js\";\n\nexport { Switch, SwitchThumb } from \"./Switch.js\";\nexport type { SwitchProps, SwitchState, SwitchThumbProps } from \"./Switch.js\";\n\nexport {\n  initialActiveIndex,\n  radioItems,\n  RadioGroup,\n  RadioGroupItem,\n} from \"./RadioGroup.js\";\nexport type {\n  RadioGroupItemProps,\n  RadioGroupProps,\n  RadioGroupState,\n  RadioItemState,\n  RadioOption,\n} from \"./RadioGroup.js\";\n\nexport {\n  Progress,\n  ProgressIndicator,\n  ProgressLabel,\n  ProgressTrack,\n  ProgressValue,\n} from \"./Progress.js\";\nexport type {\n  ProgressIndicatorProps,\n  ProgressLabelProps,\n  ProgressProps,\n  ProgressTrackProps,\n  ProgressValueProps,\n} from \"./Progress.js\";\n\nexport { Rating, ratingMax } from \"./Rating.js\";\nexport type { RatingProps, RatingState } from \"./Rating.js\";\n\nexport {\n  RangeSlider,\n  Slider,\n  SliderIndicator,\n  SliderThumb,\n  SliderTrack,\n  sliderRange,\n  sliderValueText,\n} from \"./Slider.js\";\nexport type {\n  RangeSliderProps,\n  SliderIndicatorProps,\n  SliderProps,\n  SliderState,\n  SliderThumbProps,\n  SliderTrackProps,\n} from \"./Slider.js\";\n\nexport {\n  Meter,\n  MeterIndicator,\n  MeterLabel,\n  MeterTrack,\n  MeterValue,\n} from \"./Meter.js\";\nexport type {\n  MeterIndicatorProps,\n  MeterLabelProps,\n  MeterProps,\n  MeterTrackProps,\n  MeterValueProps,\n} from \"./Meter.js\";\n\nexport { Skeleton } from \"./Skeleton.js\";\nexport type { SkeletonProps } from \"./Skeleton.js\";\n\nexport { Spinner } from \"./Spinner.js\";\nexport type { SpinnerProps } from \"./Spinner.js\";\n\nexport {\n  isSelectGroup,\n  Select,\n  selectContentStyle,\n  selectEntries,\n  selectItemStyle,\n  selectLabelStyle,\n  selectOptions,\n  selectSeparatorStyle,\n  selectTriggerStyle,\n  selectValueLabel,\n} from \"./Select.js\";\nexport type {\n  SelectEntry,\n  SelectItemState,\n  SelectOption,\n  SelectOptionGroup,\n  SelectProps,\n  SelectTriggerState,\n} from \"./Select.js\";\n\nexport { Tooltip, TooltipProvider } from \"./Tooltip.js\";\nexport type { TooltipProps, TooltipProviderProps } from \"./Tooltip.js\";\n"
    },
    {
      "path": "packages/primitives/src/components/internal.ts",
      "target": "@ui/gpuixui/primitives/components/internal.ts",
      "type": "registry:lib",
      "content": "import {\n  Children,\n  createContext,\n  useEffect,\n  useState,\n  type ReactNode,\n  type Ref,\n} from \"react\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport type {\n  EventPayload,\n  KeyEvent,\n  Props,\n  PublicInstance,\n  StyleDesc,\n} from \"@gpuix/react\";\nimport {\n  initValueState,\n  setValue,\n  withValue,\n  type ValueState,\n} from \"../core/state.js\";\nimport type { Density, PrimitiveTheme } from \"../core/theme.js\";\n\nexport const childrenName = (children: ReactNode): string | undefined => {\n  const text = Children.toArray(children)\n    .filter(\n      (child): child is string | number =>\n        typeof child === \"string\" || typeof child === \"number\",\n    )\n    .join(\" \")\n    .trim();\n  return text === \"\" ? undefined : text;\n};\n\nexport const mergeStyle = (\n  base: StyleDesc,\n  override: StyleDesc | undefined,\n): StyleDesc => (override === undefined ? base : { ...base, ...override });\n\nexport interface InteractionProps {\n  readonly onClick?: (event: EventPayload) => void;\n  readonly onKeyDown?: (event: KeyEvent) => void;\n  readonly onKeyUp?: (event: KeyEvent) => void;\n  readonly onFocus?: (event: EventPayload) => void;\n  readonly onBlur?: (event: EventPayload) => void;\n}\n\nexport interface ElementHostProps {\n  readonly ref?: Ref<PublicInstance>;\n\n  readonly \"aria-id\"?: string;\n\n  readonly onLayout?: (event: EventPayload) => void;\n}\n\nexport type FieldHostProps = ElementHostProps;\n\nexport interface HelpProps {\n  readonly description?: string;\n}\n\nexport const mergeRefs = <Instance>(\n  ...refs: ReadonlyArray<Ref<Instance> | undefined>\n): ((instance: Instance | null) => void) => {\n  return (instance) => {\n    Arr.forEach(refs, (ref) => {\n      if (ref === undefined || ref === null) return;\n      if (typeof ref === \"function\") ref(instance);\n      else ref.current = instance;\n    });\n  };\n};\n\nexport const AttachedGroup = createContext(false);\n\nexport type ActionHostProps = Omit<\n  Props,\n  | \"key\"\n  | \"children\"\n  | \"style\"\n  | \"role\"\n  | \"tabIndex\"\n  | \"aria-label\"\n  | \"aria-description\"\n  | \"motion\"\n  | \"testId\"\n  | \"onClick\"\n  | \"onKeyDown\"\n  | \"onKeyUp\"\n  | \"onFocus\"\n  | \"onBlur\"\n  | \"onMouseEnter\"\n  | \"onMouseLeave\"\n>;\n\nexport interface TooltipTriggerProps {\n  readonly onMouseEnter?: (event: EventPayload) => void;\n  readonly onMouseLeave?: (event: EventPayload) => void;\n  readonly \"aria-description\"?: string;\n}\n\nexport const composeEvent = <Event>(\n  caller: ((event: Event) => void) | undefined,\n  internal: ((event: Event) => void) | undefined,\n): ((event: Event) => void) | undefined =>\n  caller === undefined\n    ? internal\n    : internal === undefined\n      ? caller\n      : (event) => {\n          caller(event);\n          internal(event);\n        };\n\nexport const composePress = (\n  blocked: boolean,\n  caller: ((event: EventPayload) => void) | undefined,\n  action: (event: EventPayload) => void,\n): ((event: EventPayload) => void) | undefined =>\n  blocked\n    ? undefined\n    : (event) => {\n        caller?.(event);\n        action(event);\n      };\n\nexport const suppressFeedback = (style: StyleDesc): StyleDesc => ({\n  ...style,\n  hover: { backgroundColor: style.backgroundColor },\n  active: { backgroundColor: style.backgroundColor },\n});\n\nexport const suppressSelectionFeedback = (style: StyleDesc): StyleDesc => ({\n  ...style,\n  hover: {\n    backgroundColor: style.backgroundColor,\n    borderColor: style.borderColor,\n  },\n  active: {},\n});\n\nexport const decorative = (style: StyleDesc): StyleDesc =>\n  mergeStyle(style, { pointerEvents: \"none\" });\n\nexport const withDensity = (\n  theme: PrimitiveTheme,\n  density: Density | null | undefined,\n): PrimitiveTheme =>\n  density === null || density === undefined ? theme : { ...theme, density };\n\nexport interface FocusState {\n  readonly focused: boolean;\n  readonly onFocus: () => void;\n  readonly onBlur: () => void;\n}\n\nexport const useFocus = (): FocusState => {\n  const [focused, setFocused] = useState(false);\n  return {\n    focused,\n    onFocus: () => setFocused(true),\n    onBlur: () => setFocused(false),\n  };\n};\n\nexport interface CheckedChangeProps {\n  readonly onCheckedChange?: (next: boolean) => void;\n\n  readonly onChange?: (next: boolean) => void;\n}\n\nexport const checkedChange = (\n  props: CheckedChangeProps,\n): ((next: boolean) => void) | undefined =>\n  props.onCheckedChange ?? props.onChange;\n\nexport interface ValueChangeProps<Value> {\n  readonly onValueChange?: (next: Value) => void;\n\n  readonly onChange?: (next: Value) => void;\n}\n\nexport const valueChange = <Value>(\n  props: ValueChangeProps<Value>,\n): ((next: Value) => void) | undefined => props.onValueChange ?? props.onChange;\n\nexport interface ControlState<Value> {\n  readonly value: ValueState<Value>;\n  readonly set: (next: Value) => void;\n}\n\nexport const useControlState = <Value>(\n  value: Value | undefined,\n  defaultValue: Value,\n  onChange: ((next: Value) => void) | undefined,\n): ControlState<Value> => {\n  const [state, setState] = useState(() =>\n    initValueState(Option.fromNullishOr(value), defaultValue),\n  );\n  const current = withValue(state, Option.fromNullishOr(value));\n  return {\n    value: current,\n    set: (next) => {\n      const applied = setValue(current, next);\n      setState(applied.state);\n      if (applied.notify) onChange?.(next);\n    },\n  };\n};\n\nexport const useStep = (\n  steps: number,\n  intervalMs: number,\n  running = true,\n): number => {\n  const [step, setStep] = useState(0);\n  useEffect(() => {\n    if (!running) return undefined;\n    const id = setInterval(\n      () => setStep((current) => (current + 1) % steps),\n      intervalMs,\n    );\n    return () => clearInterval(id);\n  }, [steps, intervalMs, running]);\n  return step;\n};\n"
    },
    {
      "path": "packages/primitives/src/core/a11y.ts",
      "target": "@ui/gpuixui/primitives/core/a11y.ts",
      "type": "registry:lib",
      "content": "import * as Option from \"effect/Option\";\nimport * as Str from \"effect/String\";\n\nexport const accessibleName = (label: string | undefined): string | undefined =>\n  Option.getOrUndefined(\n    Option.filter(\n      Option.map(Option.fromNullishOr(label), Str.trim),\n      Str.isNonEmpty,\n    ),\n  );\n\nexport const visibleCopy = (text: string | undefined): string | undefined =>\n  accessibleName(text) === undefined ? undefined : text;\n\nexport const captionCopy = <Child>(\n  children: Child | undefined,\n  label: string | undefined,\n): Child | string | undefined =>\n  typeof children === \"string\"\n    ? (visibleCopy(children) ?? visibleCopy(label))\n    : (children ?? visibleCopy(label));\n\nexport const spokenText = (\n  words: Option.Option<string>,\n  fallback: string,\n): string =>\n  Option.getOrElse(\n    Option.flatMap(words, (word) => Option.fromNullishOr(accessibleName(word))),\n    () => fallback,\n  );\n\nexport const spokenSentence = (answer: string, fallback: string): string =>\n  spokenText(Option.fromNullishOr(answer), fallback);\n\nexport const spokenWord = (\n  word: string | undefined,\n  fallback: string,\n): string => spokenText(Option.fromNullishOr(word), fallback);\n\nexport const fieldHelp = (\n  props: { readonly description?: string; readonly placeholder?: string },\n  value: string,\n): string | undefined =>\n  Option.match(Option.fromNullishOr(accessibleName(props.description)), {\n    onSome: (help) => help,\n    onNone: () =>\n      value.length === 0 ? accessibleName(props.placeholder) : undefined,\n  });\n"
    },
    {
      "path": "packages/primitives/src/core/focus.ts",
      "target": "@ui/gpuixui/primitives/core/focus.ts",
      "type": "registry:lib",
      "content": "import type { StyleDesc } from \"@gpuix/react\";\nimport { controlFocusRing } from \"../../tokens/index.js\";\nimport type { PrimitiveTheme } from \"./theme.js\";\n\nexport const keyboardRing = (\n  theme: PrimitiveTheme,\n  invalid?: boolean,\n): StyleDesc => controlFocusRing(theme, { invalid }) ?? {};\n\nexport const keepContent: StyleDesc = { keyboardFocusDim: false };\n\nexport interface RingState {\n  readonly disabled?: boolean;\n  readonly invalid?: boolean;\n}\n\nexport const controlRing = (\n  theme: PrimitiveTheme,\n  state: RingState = {},\n): StyleDesc => controlFocusRing(theme, state) ?? {};\n"
    },
    {
      "path": "packages/primitives/src/core/index.ts",
      "target": "@ui/gpuixui/primitives/core/index.ts",
      "type": "registry:lib",
      "content": "export { alpha } from \"../../tokens/index.js\";\nexport {\n  accessibleName,\n  fieldHelp,\n  spokenSentence,\n  spokenText,\n  spokenWord,\n  visibleCopy,\n} from \"./a11y.js\";\n\nexport {\n  busyWord,\n  defaultMessages,\n  indeterminateWord,\n  pressWord,\n  rangeThumbNames,\n  resolveMessages,\n  selectionWord,\n  starWord,\n} from \"./messages.js\";\nexport type {\n  LoadingWords,\n  MessagesSource,\n  PressWords,\n  PrimitiveMessages,\n  RangeEndWords,\n  SelectionWords,\n  StarWords,\n} from \"./messages.js\";\n\nexport {\n  decideAbsolute,\n  decideActivation,\n  decideMove,\n  decidePage,\n  decideToggle,\n  decideTypeAhead,\n  ENTER_KEY,\n  isOrientation,\n  isPlainKey,\n  isTypeAheadKey,\n  orientationOf,\n  SPACE_KEY,\n  TYPE_AHEAD_RESET_MS,\n} from \"./keyboard.js\";\nexport type { KeyChord, KeyName, KeyPhase, Orientation } from \"./keyboard.js\";\n\nexport { initValueState, setValue, withValue } from \"./state.js\";\nexport type { ControlMode, ValueState } from \"./state.js\";\n\nexport { rovingIndex } from \"./roving.js\";\n\nexport { SCROLL_END, scrollTarget } from \"./scroll.js\";\nexport type { ScrollMetrics, ScrollOffset } from \"./scroll.js\";\n\nexport {\n  clampNumeric,\n  decideNumberKey,\n  displayNumber,\n  expandedNumber,\n  formatNumber,\n  normalizeNumberRange,\n  numberDisplay,\n  parseNumberText,\n  parseNumericText,\n  SCRUB_PIXELS_PER_STEP,\n  scrubNumber,\n  SHIFT_SCRUB_SCALE,\n  stepNumber,\n} from \"./number.js\";\nexport type { NumberRange, ParseValue } from \"./number.js\";\n\nexport {\n  clampRating,\n  decideRating,\n  pointerRating,\n  ratingTabStop,\n  starCount,\n  starFill,\n  starState,\n} from \"./rating.js\";\nexport type { StarBox, StarState } from \"./rating.js\";\n\nexport {\n  applyOtpInput,\n  DEFAULT_OTP_LENGTH,\n  decideOtpKey,\n  normalizeOtp,\n  otpCellCount,\n  otpCells,\n  otpEmptiedCell,\n  otpLength,\n} from \"./otp.js\";\nexport type { OtpEdit } from \"./otp.js\";\n\nexport {\n  storedList,\n  storedNumber,\n  storedText,\n  storedTextList,\n} from \"./stored.js\";\n\nexport {\n  clampValue,\n  decideValueKey,\n  fractionValue,\n  moveSpanEnd,\n  nearestEnd,\n  normalizeRange,\n  normalizeSpan,\n  pointerValue,\n  spanFrom,\n  stepValue,\n  storedRange,\n  valueFraction,\n  valueStep,\n  verticalPointerValue,\n} from \"./value.js\";\nexport type {\n  StoredRange,\n  TrackBox,\n  ValueRange,\n  ValueSpan,\n  VerticalTrackBox,\n} from \"./value.js\";\n\nexport {\n  boxStyle,\n  gridStyle,\n  isStackDirection,\n  stackDirection,\n  stackStyle,\n  STACK_ALIGNS,\n  STACK_DIRECTIONS,\n  STACK_JUSTIFIES,\n} from \"./layout.js\";\nexport type {\n  BoxSpec,\n  GridSpec,\n  StackAlign,\n  StackDirection,\n  StackJustify,\n  StackSpec,\n} from \"./layout.js\";\n\nexport {\n  isSelectionAccent,\n  radioDotStyle,\n  SELECTION_ACCENTS,\n  SELECTION_STATES,\n  selectionAccentOf,\n  selectionBoxStyle,\n  selectionMarkColor,\n  switchThumbStyle,\n  switchTrackStyle,\n  toggleSelection,\n} from \"./selection.js\";\nexport type {\n  SelectionAccent,\n  SelectionSpec,\n  SelectionState,\n  SwitchPaintTheme,\n} from \"./selection.js\";\n\nexport {\n  indeterminateFillStyle,\n  indeterminateLeadStyle,\n  indeterminateOffset,\n  progressAnimates,\n  PULSE_INTERVAL_MS,\n  PULSE_STEPS,\n  pulseOpacity,\n  skeletonSize,\n  isSpinnerAccent,\n  skeletonStyle,\n  SPINNER_ACCENTS,\n  SPINNER_ARC_TICKS,\n  SPINNER_FADE_FLOOR,\n  SPINNER_RING_TICKS,\n  SPINNER_SEGMENTS,\n  spinnerAccentOf,\n  spinnerTickStyle,\n  spinnerTicks,\n  STEP_INTERVAL_MS,\n} from \"./loading.js\";\nexport type {\n  SkeletonSpec,\n  SpinnerAccent,\n  SpinnerSpec,\n  SpinnerTick,\n} from \"./loading.js\";\n\nexport { controlRing, keyboardRing, keepContent } from \"./focus.js\";\nexport type { RingState } from \"./focus.js\";\n\nexport { pressWash, primaryButton } from \"./pointer.js\";\n\nexport {\n  BORDER_STYLES,\n  controlFocusStyle,\n  controlStyle,\n  fieldTextStyle,\n  nativeTextFills,\n  nativeTextTheme,\n  numberInputStyle,\n  numberStepperColumnStyle,\n  numberStepperStyle,\n  otpCellStyle,\n  otpGroupStyle,\n  isProgressSize,\n  isProgressTone,\n  isTextAlign,\n  isTextTone,\n  progressFillStyle,\n  progressPercent,\n  progressSizeOf,\n  progressToneOf,\n  progressTrackStyle,\n  PROGRESS_SIZES,\n  ratingStarColor,\n  ratingStarStyle,\n  ratingPartialCellStyle,\n  ratingPartialStyle,\n  PROGRESS_TONES,\n  ruleStyle,\n  SCROLL_AXES,\n  isScrollAxis,\n  scrollAxis,\n  scrollStyle,\n  sliderFillStyle,\n  sliderInset,\n  sliderRailStyle,\n  SLIDER_ORIENTATIONS,\n  sliderThumbStyle,\n  sliderTrackStyle,\n  surfaceStyle,\n  SURFACE_TONES,\n  textAlignOf,\n  textAlignValue,\n  textStyle,\n  TEXT_ALIGNMENTS,\n  textToneOf,\n  TEXT_TONES,\n  toneColor,\n} from \"./style.js\";\nexport type {\n  BorderStyle,\n  ControlSpec,\n  ControlVariant,\n  NativeTextFill,\n  NativeTextTheme,\n  OtpCellSpec,\n  ProgressSize,\n  ProgressSpec,\n  ProgressTone,\n  RatingStarSpec,\n  RuleSpec,\n  ScrollAxis,\n  ScrollSpec,\n  SliderOrientation,\n  SliderSpec,\n  SurfaceSpec,\n  SurfaceTone,\n  TextAlign,\n  TextSpec,\n  TextTone,\n} from \"./style.js\";\n\nexport {\n  actionOnColor,\n  buttonVariants,\n  buttonVariantOf,\n  BUTTON_VARIANTS,\n  fieldVariant,\n  hasPointerFeedback,\n  isButtonVariant,\n  isToggleVariant,\n  TOGGLE_VARIANTS,\n  toggleVariantOf,\n  toggleVariants,\n  variantKey,\n  variantTable,\n} from \"./variants.js\";\nexport type { ButtonVariant, ToggleVariant } from \"./variants.js\";\n\nexport {\n  borderWidthOf,\n  colorRole,\n  colorsOf,\n  controlHeightOf,\n  controlPaddingOf,\n  controlSize,\n  elevationLevel,\n  familyOf,\n  iconSideOf,\n  iconStepValue,\n  isIconStep,\n  isRadiusStep,\n  isSpaceStep,\n  isTextRole,\n  radiusStep,\n  radiusValue,\n  rowHeightOf,\n  spaceStep,\n  spaceValue,\n  typeOf,\n} from \"./theme.js\";\n\nexport type {\n  BorderWidth,\n  BorderWidthSource,\n  ColorRef,\n  ColorSource,\n  ControlSize,\n  Density,\n  DensityName,\n  DensitySource,\n  Elevation,\n  ElevationLevel,\n  FontWeight,\n  IconSizeSource,\n  PrimitiveTheme,\n  Radius,\n  RadiusSource,\n  RadiusStep,\n  Spacing,\n  SpaceSource,\n  SpaceStep,\n  TextRole,\n  TextStyle,\n  ThemeColors,\n  ThemeIconSize,\n  ThemeMode,\n  ThemeShadow,\n  TypeSource,\n  Typography,\n} from \"./theme.js\";\n"
    },
    {
      "path": "packages/primitives/src/core/keyboard.ts",
      "target": "@ui/gpuixui/primitives/core/keyboard.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport * as R from \"effect/Record\";\nimport * as Str from \"effect/String\";\n\nexport type KeyName = string;\n\nexport type KeyPhase = \"down\" | \"up\";\n\nexport const ENTER_KEY = \"enter\";\nexport const SPACE_KEY = \"space\";\n\nexport const swallowKeyDefault = (key: KeyName, phase: KeyPhase): boolean =>\n  key === SPACE_KEY && phase === \"down\";\n\nexport interface KeyChord {\n  readonly shift?: boolean;\n  readonly ctrl?: boolean;\n  readonly alt?: boolean;\n  readonly cmd?: boolean;\n}\n\nexport const isPlainKey = (chord: KeyChord | undefined): boolean =>\n  chord?.ctrl !== true && chord?.alt !== true && chord?.cmd !== true;\n\nexport const decideActivation = (\n  key: KeyName,\n  phase: KeyPhase,\n  held: boolean,\n): boolean => {\n  if (held) return false;\n  if (key === ENTER_KEY) return phase === \"down\";\n  if (key === SPACE_KEY) return phase === \"up\";\n  return false;\n};\n\nexport const decideToggle = (current: boolean): boolean => !current;\n\nconst ARROW_KEYS = {\n  vertical: { forward: \"down\", backward: \"up\" },\n  horizontal: { forward: \"right\", backward: \"left\" },\n} as const;\n\nexport type Orientation = keyof typeof ARROW_KEYS;\n\nconst ORIENTATION_NAMES: ReadonlyArray<string> = R.keys(ARROW_KEYS);\n\nexport const isOrientation = (value: unknown): value is Orientation =>\n  P.isString(value) && Arr.contains(ORIENTATION_NAMES, value);\n\nexport const orientationOf = (\n  value: unknown,\n  fallback: Orientation,\n): Orientation => (isOrientation(value) ? value : fallback);\n\nexport const decideMove = (\n  index: number,\n  count: number,\n  key: KeyName,\n  orientation: Orientation,\n  isDisabled: (index: number) => boolean = () => false,\n): Option.Option<number> => {\n  if (count <= 1 || !isOrientation(orientation)) return Option.none();\n  const { forward, backward } = ARROW_KEYS[orientation];\n  if (key !== forward && key !== backward) return Option.none();\n  const step = key === forward ? 1 : -1;\n  const candidates = Arr.map(\n    Arr.range(1, count - 1),\n    (offset) => (((index + step * offset) % count) + count) % count,\n  );\n  return Arr.findFirst(candidates, (candidate) => !isDisabled(candidate));\n};\n\nexport const decideAbsolute = (\n  count: number,\n  key: KeyName,\n  isDisabled: (index: number) => boolean = () => false,\n): Option.Option<number> => {\n  if (count <= 0) return Option.none();\n  const indexes = Arr.range(0, count - 1);\n  if (key === \"home\")\n    return Arr.findFirst(indexes, (index) => !isDisabled(index));\n  if (key === \"end\")\n    return Arr.findFirst(Arr.reverse(indexes), (index) => !isDisabled(index));\n  return Option.none();\n};\n\nexport const decidePage = (\n  index: number,\n  count: number,\n  key: KeyName,\n  pageSize: number,\n): Option.Option<number> => {\n  if (count <= 0 || pageSize <= 0) return Option.none();\n  if (key !== \"pageup\" && key !== \"pagedown\") return Option.none();\n  const target = key === \"pagedown\" ? index + pageSize : index - pageSize;\n  return Option.some(Math.max(0, Math.min(count - 1, target)));\n};\n\nexport const TYPE_AHEAD_RESET_MS = 500;\n\nexport const isTypeAheadKey = (key: KeyName): boolean => key.length === 1;\n\nexport const decideTypeAhead = (\n  labels: ReadonlyArray<string>,\n  from: number,\n  buffer: string,\n  isDisabled: (index: number) => boolean = () => false,\n): Option.Option<number> => {\n  const count = labels.length;\n  if (count <= 0 || buffer.length === 0) return Option.none();\n  const search = (wanted: string): Option.Option<number> => {\n    const order = Arr.map(\n      Arr.range(1, count),\n      (offset) => (((from + offset) % count) + count) % count,\n    );\n    return Arr.findFirst(\n      order,\n      (index) =>\n        !isDisabled(index) &&\n        Str.startsWith(wanted)(Str.toLowerCase(labels[index] ?? \"\")),\n    );\n  };\n  const wanted = Str.toLowerCase(buffer);\n  return Option.match(search(wanted), {\n    onSome: Option.some,\n    onNone: () =>\n      wanted.length > 1 ? search(Str.takeRight(1)(wanted)) : Option.none(),\n  });\n};\n"
    },
    {
      "path": "packages/primitives/src/core/layout.ts",
      "target": "@ui/gpuixui/primitives/core/layout.ts",
      "type": "registry:lib",
      "content": "import type { StyleDesc } from \"@gpuix/react\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport { storedNumber } from \"./stored.js\";\nimport { spaceValue, type PrimitiveTheme, type SpaceStep } from \"./theme.js\";\nimport { surfaceStyle, type SurfaceSpec } from \"./style.js\";\n\nexport const STACK_DIRECTIONS = [\"row\", \"column\"] as const;\nexport type StackDirection = (typeof STACK_DIRECTIONS)[number];\n\nexport const isStackDirection = (value: unknown): value is StackDirection =>\n  P.isString(value) && Arr.contains(STACK_DIRECTIONS, value);\n\nexport const stackDirection = (value: unknown): StackDirection =>\n  isStackDirection(value) ? value : \"column\";\n\nexport const STACK_ALIGNS = [\n  \"start\",\n  \"center\",\n  \"end\",\n  \"stretch\",\n  \"baseline\",\n] as const;\nexport type StackAlign = (typeof STACK_ALIGNS)[number];\n\nexport const STACK_JUSTIFIES = [\n  \"start\",\n  \"center\",\n  \"end\",\n  \"between\",\n  \"around\",\n] as const;\nexport type StackJustify = (typeof STACK_JUSTIFIES)[number];\n\nconst alignToFlex: Record<StackAlign, string> = {\n  start: \"flex-start\",\n  center: \"center\",\n  end: \"flex-end\",\n  stretch: \"stretch\",\n  baseline: \"baseline\",\n};\n\nconst justifyToFlex: Record<StackJustify, string> = {\n  start: \"flex-start\",\n  center: \"center\",\n  end: \"flex-end\",\n  between: \"space-between\",\n  around: \"space-around\",\n};\n\nexport interface StackSpec {\n  readonly direction: StackDirection;\n\n  readonly gap?: SpaceStep;\n  readonly align?: StackAlign;\n  readonly justify?: StackJustify;\n  readonly wrap?: boolean;\n\n  readonly grow?: boolean;\n  readonly fullWidth?: boolean;\n  readonly fullHeight?: boolean;\n}\n\nexport const stackStyle = (\n  spec: StackSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const style: StyleDesc = {\n    display: \"flex\",\n    flexDirection: stackDirection(spec.direction),\n  };\n  if (spec.gap !== undefined) style.gap = spaceValue(theme, spec.gap);\n  if (spec.align !== undefined) style.alignItems = alignToFlex[spec.align];\n  if (spec.justify !== undefined)\n    style.justifyContent = justifyToFlex[spec.justify];\n  if (spec.wrap === true) style.flexWrap = \"wrap\";\n  if (spec.grow === true) style.flexGrow = 1;\n  if (spec.fullWidth === true) style.width = \"100%\";\n  if (spec.fullHeight === true) style.height = \"100%\";\n  return style;\n};\n\nexport interface GridSpec {\n  readonly columns: number;\n  readonly rows?: number;\n  readonly gap?: SpaceStep;\n  readonly grow?: boolean;\n}\n\nexport const trackCount = (count: number): number =>\n  Number.isFinite(count) ? Math.max(1, Math.floor(count)) : 1;\n\nexport interface FieldRows {\n  readonly minRows?: number;\n  readonly maxRows?: number;\n}\n\nconst rowCount = (count: unknown): Option.Option<number> =>\n  Option.map(storedNumber(count), trackCount);\n\nexport const fieldRows = (minRows: unknown, maxRows: unknown): FieldRows => {\n  const floor = rowCount(minRows);\n  const cap = rowCount(maxRows);\n  const raised = Option.match(Option.all([floor, cap]), {\n    onNone: () => cap,\n    onSome: ([floorRows, capRows]) => Option.some(Math.max(capRows, floorRows)),\n  });\n  return {\n    minRows: Option.getOrUndefined(floor),\n    maxRows: Option.getOrUndefined(raised),\n  };\n};\n\nexport const gridStyle = (spec: GridSpec, theme: PrimitiveTheme): StyleDesc => {\n  const style: StyleDesc = {\n    display: \"grid\",\n    gridTemplateColumns: trackCount(spec.columns),\n  };\n  if (spec.rows !== undefined) {\n    style.gridTemplateRows = trackCount(spec.rows);\n  }\n  if (spec.gap !== undefined) style.gap = spaceValue(theme, spec.gap);\n  if (spec.grow === true) style.flexGrow = 1;\n  return style;\n};\n\nexport interface BoxSpec extends SurfaceSpec {\n  readonly grow?: boolean;\n\n  readonly shrink?: boolean;\n  readonly fullWidth?: boolean;\n  readonly fullHeight?: boolean;\n}\n\nexport const boxStyle = (spec: BoxSpec, theme: PrimitiveTheme): StyleDesc => {\n  const style: StyleDesc = {\n    ...surfaceStyle(spec, theme),\n    flexShrink: spec.shrink === true ? 1 : 0,\n  };\n  if (spec.grow === true) style.flexGrow = 1;\n  if (spec.fullWidth === true) style.width = \"100%\";\n  if (spec.fullHeight === true) style.height = \"100%\";\n  return style;\n};\n"
    },
    {
      "path": "packages/primitives/src/core/loading.ts",
      "target": "@ui/gpuixui/primitives/core/loading.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport { motionLevels } from \"../../motion/timing.js\";\nimport { visibleCopy } from \"./a11y.js\";\nimport type { PrimitiveTheme, RadiusStep } from \"./theme.js\";\nimport { colorRole, radiusStep, radiusValue, typeOf } from \"./theme.js\";\n\nexport const PULSE_STEPS = 2;\n\nexport const SPINNER_SEGMENTS = 8;\n\nexport const SPINNER_RING_TICKS = 12;\n\nexport const SPINNER_ARC_TICKS = 7;\n\nexport const STEP_INTERVAL_MS = motionLevels.step;\n\nexport const PULSE_INTERVAL_MS = 700;\n\nexport const SPINNER_FADE_FLOOR = 0.2;\n\nexport interface SkeletonSpec {\n  readonly width?: number | string;\n  readonly height?: number | string;\n\n  readonly radius?: RadiusStep;\n\n  readonly circle?: boolean;\n}\n\nconst paintableSide = (\n  side: number | string | undefined,\n): number | string | undefined =>\n  typeof side === \"number\"\n    ? Number.isFinite(side) && side > 0\n      ? side\n      : undefined\n    : visibleCopy(side);\n\nexport const skeletonSize = (\n  spec: SkeletonSpec,\n  theme: PrimitiveTheme,\n): { readonly width: number | string; readonly height: number | string } => {\n  const width = paintableSide(spec.width);\n  const height = paintableSide(spec.height);\n  const line = typeOf(theme, \"body\").lineHeight;\n  if (spec.circle !== true) {\n    return {\n      width: width ?? \"100%\",\n      height: height ?? line,\n    };\n  }\n  if (width !== undefined && height !== undefined) {\n    return { width, height };\n  }\n  const side = width ?? height ?? line;\n  return { width: side, height: side };\n};\n\nexport const skeletonStyle = (\n  spec: SkeletonSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => ({\n  ...skeletonSize(spec, theme),\n  flexShrink: 0,\n  borderRadius:\n    spec.circle === true\n      ? radiusValue(theme, \"full\")\n      : radiusValue(theme, radiusStep(spec.radius) ?? \"sm\"),\n  backgroundColor: colorRole(theme, \"skeleton\"),\n});\n\nexport const pulseOpacity = (step: number, dim = 0.6, full = 1): number =>\n  step % PULSE_STEPS === 0 ? full : dim;\n\nexport const SPINNER_ACCENTS = [\n  \"primary\",\n  \"destructive\",\n  \"muted\",\n  \"on-primary\",\n  \"on-secondary\",\n  \"on-destructive\",\n  \"on-accent\",\n  \"on-disabled\",\n] as const;\nexport type SpinnerAccent = (typeof SPINNER_ACCENTS)[number];\n\nconst ACCENT_NAMES: ReadonlyArray<string> = SPINNER_ACCENTS;\n\nexport const isSpinnerAccent = (value: unknown): value is SpinnerAccent =>\n  typeof value === \"string\" && Arr.contains(ACCENT_NAMES, value);\n\nexport const spinnerAccentOf = (value: unknown): SpinnerAccent =>\n  isSpinnerAccent(value) ? value : \"primary\";\n\nexport interface SpinnerSpec {\n  readonly size: number;\n  readonly accent: SpinnerAccent;\n}\n\nexport interface SpinnerTick {\n  readonly index: number;\n\n  readonly active: boolean;\n  readonly opacity: number;\n\n  readonly x: number;\n  readonly y: number;\n\n  readonly side: number;\n}\n\nexport const spinnerTicks = (\n  step: number,\n  size: number,\n  count = SPINNER_RING_TICKS,\n  arc = SPINNER_ARC_TICKS,\n): ReadonlyArray<SpinnerTick> => {\n  const ring = Math.max(\n    2,\n    Math.trunc(Number.isFinite(count) ? count : SPINNER_RING_TICKS),\n  );\n  const lit = Math.min(\n    ring,\n    Math.max(1, Math.trunc(Number.isFinite(arc) ? arc : SPINNER_ARC_TICKS)),\n  );\n  const side = Number.isFinite(size) && size > 0 ? size : 16;\n  const tick = Math.max(1.5, side / 6);\n  const radius = (side - tick) / 2;\n  const head = ((Math.trunc(step) % ring) + ring) % ring;\n  return Arr.map(Arr.range(0, ring - 1), (index) => {\n    const distance = (head - index + ring) % ring;\n    const angle = -Math.PI / 2 + (index / ring) * Math.PI * 2;\n    const centre = side / 2;\n\n    const edge = side - tick;\n    const x = Math.min(\n      edge,\n      Math.max(0, centre + radius * Math.cos(angle) - tick / 2),\n    );\n    const y = Math.min(\n      edge,\n      Math.max(0, centre + radius * Math.sin(angle) - tick / 2),\n    );\n    const opacity =\n      distance >= lit\n        ? 0\n        : distance === lit - 1\n          ? SPINNER_FADE_FLOOR\n          : 1 - (distance / Math.max(1, lit - 1)) * (1 - SPINNER_FADE_FLOOR);\n    return {\n      index,\n      active: distance === 0,\n      opacity,\n      side: tick,\n      x,\n      y,\n    };\n  });\n};\n\nconst ACCENT_COLOR: Readonly<\n  Record<SpinnerAccent, (theme: PrimitiveTheme) => string | undefined>\n> = {\n  primary: (theme) => colorRole(theme, \"primary\"),\n  destructive: (theme) => colorRole(theme, \"destructive\"),\n  muted: (theme) => colorRole(theme, \"mutedForeground\"),\n  \"on-primary\": (theme) => colorRole(theme, \"primaryForeground\"),\n  \"on-secondary\": (theme) => colorRole(theme, \"secondaryForeground\"),\n  \"on-destructive\": (theme) => colorRole(theme, \"destructiveForeground\"),\n  \"on-accent\": (theme) => colorRole(theme, \"accentForeground\"),\n  \"on-disabled\": (theme) => colorRole(theme, \"disabledForeground\"),\n};\n\nconst accentColor = (\n  accent: SpinnerAccent,\n  theme: PrimitiveTheme,\n): string | undefined => ACCENT_COLOR[spinnerAccentOf(accent)](theme);\n\nexport const spinnerTickStyle = (\n  spec: SpinnerSpec,\n  tick: SpinnerTick,\n  theme: PrimitiveTheme,\n): StyleDesc => ({\n  position: \"absolute\",\n  left: tick.x,\n  top: tick.y,\n  width: tick.side,\n  height: tick.side,\n  flexShrink: 0,\n  borderRadius: radiusValue(theme, \"full\"),\n  opacity: tick.opacity,\n  backgroundColor: accentColor(spec.accent, theme),\n});\n\nexport const indeterminateOffset = (step: number, segment = 40): number => {\n  const travel = Math.max(0, 100 - segment);\n  const phase =\n    ((step % SPINNER_SEGMENTS) + SPINNER_SEGMENTS) % SPINNER_SEGMENTS;\n  return (phase / SPINNER_SEGMENTS) * travel;\n};\n\nexport const progressAnimates = (value: Option.Option<number>): boolean =>\n  Option.isNone(value);\n\nexport const indeterminateFillStyle = (\n  segment: number,\n  color: string,\n  theme: PrimitiveTheme,\n): StyleDesc => ({\n  width: `${segment}%`,\n  height: \"100%\",\n  borderRadius: radiusValue(theme, \"full\"),\n  backgroundColor: color,\n});\n\nexport const indeterminateLeadStyle = (offset: number): StyleDesc => ({\n  width: `${offset}%`,\n  flexShrink: 0,\n});\n"
    },
    {
      "path": "packages/primitives/src/core/messages.ts",
      "target": "@ui/gpuixui/primitives/core/messages.ts",
      "type": "registry:lib",
      "content": "import * as P from \"effect/Predicate\";\nimport { accessibleName, spokenSentence, spokenWord } from \"./a11y.js\";\nimport type { StarState } from \"./rating.js\";\nimport type { SelectionState } from \"./selection.js\";\n\nexport interface SelectionWords {\n  readonly checked: string;\n  readonly unchecked: string;\n  readonly mixed: string;\n}\n\nexport interface PressWords {\n  readonly pressed: string;\n  readonly notPressed: string;\n}\n\nexport interface StarWords {\n  readonly checked: string;\n  readonly half: string;\n  readonly unchecked: string;\n}\n\nexport interface LoadingWords {\n  readonly busy: string;\n  readonly indeterminate: string;\n}\n\nexport type RangeEndWords = (label: string, end: \"start\" | \"end\") => string;\n\nexport interface PrimitiveMessages {\n  readonly selection: SelectionWords;\n  readonly press: PressWords;\n  readonly star: StarWords;\n\n  readonly starName: (index: number, max: number) => string;\n\n  readonly scrollRegion: string;\n\n  readonly codeDigit: (index: number, label: string | undefined) => string;\n\n  readonly loading: LoadingWords;\n\n  readonly rangeEnd: RangeEndWords;\n}\n\nexport const defaultMessages: PrimitiveMessages = {\n  selection: { checked: \"checked\", unchecked: \"unchecked\", mixed: \"mixed\" },\n  press: { pressed: \"pressed\", notPressed: \"not pressed\" },\n  star: { checked: \"checked\", half: \"half\", unchecked: \"unchecked\" },\n  starName: (index, max) => `${index} of ${max}`,\n  scrollRegion: \"Scroll region\",\n  codeDigit: (index, label) => `${label ?? \"Code\"} digit ${index + 1}`,\n  loading: { busy: \"Loading\", indeterminate: \"loading\" },\n  rangeEnd: (label, end) =>\n    `${label} ${end === \"start\" ? \"minimum\" : \"maximum\"}`,\n};\n\nconst spokenEnd = (\n  label: string,\n  end: RangeEndWords,\n  side: \"start\" | \"end\",\n): string =>\n  spokenSentence(end(label, side), defaultMessages.rangeEnd(label, side));\n\nexport const rangeThumbNames = (\n  label: string | undefined,\n  end: RangeEndWords = defaultMessages.rangeEnd,\n): readonly [string | undefined, string | undefined] => {\n  const name = accessibleName(label);\n  return name === undefined\n    ? [undefined, undefined]\n    : [spokenEnd(name, end, \"start\"), spokenEnd(name, end, \"end\")];\n};\n\ntype Stored<T> = T extends (...args: never[]) => unknown\n  ? unknown\n  : T extends object\n    ? { readonly [K in keyof T]?: Stored<T[K]> } | null\n    : unknown;\n\nexport type MessagesSource = {\n  readonly [K in keyof PrimitiveMessages]?: Stored<PrimitiveMessages[K]>;\n};\n\nconst row = (stored: unknown, inherited: string): string =>\n  P.isString(stored) ? stored : inherited;\n\nconst isSentence = <A>(value: unknown): value is A => P.isFunction(value);\n\nconst sentence = <A>(stored: unknown, inherited: A): A =>\n  isSentence<A>(stored) ? stored : inherited;\n\nconst selectionRows = (\n  stored: Stored<SelectionWords> | undefined,\n  inherited: SelectionWords,\n): SelectionWords => ({\n  checked: row(stored?.checked, inherited.checked),\n  unchecked: row(stored?.unchecked, inherited.unchecked),\n  mixed: row(stored?.mixed, inherited.mixed),\n});\n\nconst pressRows = (\n  stored: Stored<PressWords> | undefined,\n  inherited: PressWords,\n): PressWords => ({\n  pressed: row(stored?.pressed, inherited.pressed),\n  notPressed: row(stored?.notPressed, inherited.notPressed),\n});\n\nconst starRows = (\n  stored: Stored<StarWords> | undefined,\n  inherited: StarWords,\n): StarWords => ({\n  checked: row(stored?.checked, inherited.checked),\n  half: row(stored?.half, inherited.half),\n  unchecked: row(stored?.unchecked, inherited.unchecked),\n});\n\nconst loadingRows = (\n  stored: Stored<LoadingWords> | undefined,\n  inherited: LoadingWords,\n): LoadingWords => ({\n  busy: row(stored?.busy, inherited.busy),\n  indeterminate: row(stored?.indeterminate, inherited.indeterminate),\n});\n\nexport const resolveMessages = (\n  messages?: MessagesSource | null,\n  inherited: PrimitiveMessages = defaultMessages,\n): PrimitiveMessages =>\n  messages === undefined || messages === null\n    ? inherited\n    : {\n        selection: selectionRows(messages.selection, inherited.selection),\n        press: pressRows(messages.press, inherited.press),\n        star: starRows(messages.star, inherited.star),\n        starName: sentence(messages.starName, inherited.starName),\n        scrollRegion: row(messages.scrollRegion, inherited.scrollRegion),\n        codeDigit: sentence(messages.codeDigit, inherited.codeDigit),\n        loading: loadingRows(messages.loading, inherited.loading),\n        rangeEnd: sentence(messages.rangeEnd, inherited.rangeEnd),\n      };\n\nexport const selectionWord = (\n  state: SelectionState,\n  words: SelectionWords = defaultMessages.selection,\n): string =>\n  state === \"indeterminate\"\n    ? spokenWord(words.mixed, defaultMessages.selection.mixed)\n    : state === \"checked\"\n      ? spokenWord(words.checked, defaultMessages.selection.checked)\n      : spokenWord(words.unchecked, defaultMessages.selection.unchecked);\n\nexport const pressWord = (\n  pressed: boolean,\n  words: PressWords = defaultMessages.press,\n): string =>\n  pressed\n    ? spokenWord(words.pressed, defaultMessages.press.pressed)\n    : spokenWord(words.notPressed, defaultMessages.press.notPressed);\n\nexport const starWord = (\n  state: StarState,\n  words: StarWords = defaultMessages.star,\n): string =>\n  state === \"checked\"\n    ? spokenWord(words.checked, defaultMessages.star.checked)\n    : state === \"half\"\n      ? spokenWord(words.half, defaultMessages.star.half)\n      : spokenWord(words.unchecked, defaultMessages.star.unchecked);\n\nexport const busyWord = (\n  words: LoadingWords = defaultMessages.loading,\n): string => spokenWord(words.busy, defaultMessages.loading.busy);\n\nexport const indeterminateWord = (\n  words: LoadingWords = defaultMessages.loading,\n): string =>\n  spokenWord(words.indeterminate, defaultMessages.loading.indeterminate);\n"
    },
    {
      "path": "packages/primitives/src/core/motion.ts",
      "target": "@ui/gpuixui/primitives/core/motion.ts",
      "type": "registry:lib",
      "content": "import type { MotionTransition } from \"@gpuix/react\";\n\nexport { reducedMotionOf } from \"../../motion/timing.js\";\n\nexport const stillFrame = (step: number, reducedMotion?: boolean): number =>\n  reducedMotion === true ? 0 : step;\n\nexport const stillTransition = (\n  transition: MotionTransition,\n  reducedMotion?: boolean,\n): MotionTransition => (reducedMotion === true ? { duration: 0 } : transition);\n"
    },
    {
      "path": "packages/primitives/src/core/number.ts",
      "target": "@ui/gpuixui/primitives/core/number.ts",
      "type": "registry:lib",
      "content": "import * as Option from \"effect/Option\";\nimport * as Str from \"effect/String\";\nimport { visibleCopy } from \"./a11y.js\";\nimport { storedNumber } from \"./stored.js\";\n\nexport interface NumberRange {\n  readonly min?: number;\n  readonly max?: number;\n  readonly step: number;\n}\n\nconst NUMERIC = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?$/i;\n\nexport const parseNumericText = (text: string): Option.Option<number> => {\n  const trimmed = text.trim();\n  if (!NUMERIC.test(trimmed)) return Option.none();\n  const value = Number(trimmed);\n  return Number.isFinite(value) ? Option.some(value) : Option.none();\n};\n\nexport type ParseValue = (text: string) => Option.Option<number>;\n\nexport const parseNumberText = (\n  text: string,\n  parseValue?: ParseValue,\n): Option.Option<number> => {\n  if (!Str.isNonEmpty(Str.trim(text))) return Option.none();\n  const own: Option.Option<number> =\n    parseValue === undefined\n      ? Option.none()\n      : Option.filter(parseValue(text), Number.isFinite);\n  return Option.isSome(own) ? own : parseNumericText(text);\n};\n\nexport const normalizeNumberRange = (range: NumberRange): NumberRange => {\n  const min = Number.isFinite(range.min) ? range.min : undefined;\n  const max = Number.isFinite(range.max) ? range.max : undefined;\n  return min !== undefined && max !== undefined && min > max\n    ? { min: max, max: min, step: range.step }\n    : { min, max, step: range.step };\n};\n\nconst numericBounds = (\n  range: NumberRange,\n): { readonly min: number | undefined; readonly max: number | undefined } => {\n  const { min, max } = normalizeNumberRange(range);\n  return { min, max };\n};\n\nconst paintable = (\n  value: number,\n  min: number | undefined,\n  max: number | undefined,\n): number => (Number.isFinite(value) ? value : (min ?? max ?? 0));\n\nexport const clampNumeric = (value: number, range: NumberRange): number => {\n  const { min, max } = numericBounds(range);\n  const finite = paintable(value, min, max);\n  const aboveMin = min === undefined ? finite : Math.max(min, finite);\n  return max === undefined ? aboveMin : Math.min(max, aboveMin);\n};\n\nconst NEGATIVE_ZERO = /^-0(?:\\.0+)?$/;\n\nconst plainFixed = (text: string, decimals: number): string => {\n  const [mantissa = \"0\", exponentText = \"0\"] = text.split(/e/i);\n  const negative = Str.startsWith(\"-\")(mantissa);\n  const unsigned = negative ? Str.slice(1)(mantissa) : mantissa;\n  const [whole = \"0\", fraction = \"\"] = unsigned.split(\".\");\n  const shift = Number(exponentText) - Str.length(fraction);\n  const digits = `${whole}${fraction}${Str.repeat(Math.max(0, shift))(\"0\")}`;\n  const point = decimals > 0 ? `.${Str.repeat(decimals)(\"0\")}` : \"\";\n  return `${negative ? \"-\" : \"\"}${digits}${point}`;\n};\n\nexport const formatNumber = (value: number, precision = 0): string => {\n  const decimals = Math.min(20, Math.max(0, Math.trunc(precision)));\n  const fixed = value.toFixed(decimals);\n  const text = Str.includes(\"e\")(fixed) ? plainFixed(fixed, decimals) : fixed;\n  return NEGATIVE_ZERO.test(text) ? Str.slice(1)(text) : text;\n};\n\nconst writtenDecimals = (value: number): number => {\n  const [mantissa = \"\", exponentText] = Str.split(/e/i)(String(value));\n  const exponent = exponentText === undefined ? 0 : Number(exponentText);\n  const [, fraction = \"\"] = Str.split(\".\")(mantissa);\n  return Math.max(0, Str.length(fraction) - exponent);\n};\n\nconst decimalsOf = (value: number): number =>\n  Math.min(15, writtenDecimals(value));\n\nexport const expandedNumber = (value: number): string => {\n  const text = String(value);\n  if (!Str.includes(\"e\")(text)) return text;\n  const [mantissa = \"0\", exponentText = \"0\"] = Str.split(/e/i)(text);\n  const negative = Str.startsWith(\"-\")(mantissa);\n  const unsigned = negative ? Str.slice(1)(mantissa) : mantissa;\n  const [whole = \"0\", fraction = \"\"] = Str.split(\".\")(unsigned);\n  const digits = `${whole}${fraction}`;\n  const point = Str.length(whole) + Number(exponentText);\n  const body =\n    point <= 0\n      ? `0.${Str.repeat(-point)(\"0\")}${digits}`\n      : point >= Str.length(digits)\n        ? `${digits}${Str.repeat(point - Str.length(digits))(\"0\")}`\n        : `${Str.slice(0, point)(digits)}.${Str.slice(point)(digits)}`;\n  return `${negative ? \"-\" : \"\"}${body}`;\n};\n\nconst precisionOf = (precision: unknown): Option.Option<number> =>\n  Option.filter(storedNumber(precision), Number.isFinite);\n\nexport const displayNumber = (value: number, precision?: unknown): string =>\n  Option.match(precisionOf(precision), {\n    onNone: () => expandedNumber(value),\n    onSome: (decimals) => formatNumber(value, decimals),\n  });\n\nexport const numberDisplay = (\n  value: number,\n  precision: unknown,\n  formatValue?: (value: number) => string,\n): string =>\n  visibleCopy(formatValue?.(value)) ?? displayNumber(value, precision);\n\nexport const stepNumber = (\n  value: number,\n  range: NumberRange,\n  steps: number,\n): number => {\n  const step = Number.isFinite(range.step) && range.step > 0 ? range.step : 1;\n\n  const from = clampNumeric(value, range);\n  const scale = 10 ** Math.max(decimalsOf(step), decimalsOf(from));\n  const moved = Math.round((from + steps * step) * scale) / scale;\n  return clampNumeric(moved, range);\n};\n\nexport const SCRUB_PIXELS_PER_STEP = 4;\n\nexport const SHIFT_SCRUB_SCALE = 10;\n\nexport const scrubNumber = (\n  start: number,\n  travel: number,\n  range: NumberRange,\n  pixelsPerStep = SCRUB_PIXELS_PER_STEP,\n  stepScale = 1,\n): number => {\n  if (!Number.isFinite(travel)) return start;\n  const scale =\n    Number.isFinite(pixelsPerStep) && pixelsPerStep > 0\n      ? pixelsPerStep\n      : SCRUB_PIXELS_PER_STEP;\n  const multiplier =\n    Number.isFinite(stepScale) && stepScale > 0 ? stepScale : 1;\n  const steps =\n    Math.sign(travel) * Math.round(Math.abs(travel) / scale) * multiplier;\n  return stepNumber(start, range, steps);\n};\n\nconst ARROW_STEPS: Readonly<Record<string, number>> = {\n  up: 1,\n  down: -1,\n};\n\nexport const decideNumberKey = (\n  value: number,\n  range: NumberRange,\n  key: string,\n  pageSteps = 10,\n): Option.Option<number> => {\n  const { min, max } = normalizeNumberRange(range);\n  if (key === \"home\")\n    return min === undefined ? Option.none() : Option.some(min);\n  if (key === \"end\")\n    return max === undefined ? Option.none() : Option.some(max);\n  const steps =\n    key === \"pageup\"\n      ? pageSteps\n      : key === \"pagedown\"\n        ? -pageSteps\n        : ARROW_STEPS[key];\n  return steps === undefined\n    ? Option.none()\n    : Option.some(stepNumber(value, range, steps));\n};\n\nexport interface NumberDraft {\n  readonly text: Option.Option<string>;\n  readonly incoming: Option.Option<number>;\n  readonly touched: boolean;\n}\n\nexport const initNumberDraft = (\n  incoming: Option.Option<number>,\n  hasValue: boolean,\n): NumberDraft => ({\n  text: Option.none(),\n  incoming,\n  touched: hasValue || Option.isSome(incoming),\n});\n\nexport const numberDraftText = (\n  draft: NumberDraft,\n  current: number,\n  display: (value: number) => string,\n): string =>\n  Option.match(draft.text, {\n    onSome: (text) => text,\n    onNone: () => (draft.touched ? display(current) : \"\"),\n  });\n\nexport const withNumberDraftText = (\n  draft: NumberDraft,\n  text: string,\n): NumberDraft => ({ ...draft, text: Option.some(text) });\n\nexport const withNumberDraftValue = (draft: NumberDraft): NumberDraft => ({\n  ...draft,\n  touched: true,\n});\n\nexport const commitNumberDraft = (draft: NumberDraft): NumberDraft => ({\n  ...draft,\n  text: Option.none(),\n});\n\nconst meansValue = (\n  text: string,\n  range: NumberRange,\n  parseValue?: ParseValue,\n): Option.Option<number> =>\n  Option.map(parseNumberText(text, parseValue), (parsed) =>\n    clampNumeric(parsed, range),\n  );\n\nexport const syncNumberDraft = (\n  draft: NumberDraft,\n  incoming: Option.Option<number>,\n  range: NumberRange,\n  parseValue?: ParseValue,\n): Option.Option<NumberDraft> => {\n  if (Option.isNone(incoming)) return Option.none();\n  const value = incoming.value;\n\n  if (Object.is(Option.getOrUndefined(draft.incoming), value))\n    return Option.none();\n  const kept = Option.match(draft.text, {\n    onSome: (text) =>\n      Option.match(meansValue(text, range, parseValue), {\n        onNone: () => false,\n        onSome: (meant) => meant === value,\n      }),\n    onNone: () => false,\n  });\n  return Option.some({\n    text: kept ? draft.text : Option.none(),\n    incoming: Option.some(value),\n    touched: true,\n  });\n};\n"
    },
    {
      "path": "packages/primitives/src/core/otp.ts",
      "target": "@ui/gpuixui/primitives/core/otp.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Str from \"effect/String\";\nimport { storedNumber, storedText } from \"./stored.js\";\n\nexport const otpLength = (length: number): number =>\n  Math.max(1, Math.trunc(Number.isFinite(length) ? length : 1));\n\nexport const DEFAULT_OTP_LENGTH = 6;\n\nexport const otpCellCount = (length: unknown): number =>\n  Option.match(storedNumber(length), {\n    onNone: () => DEFAULT_OTP_LENGTH,\n    onSome: otpLength,\n  });\n\nexport const normalizeOtp = (value: unknown, length: number): string =>\n  Str.takeLeft(otpLength(length))(\n    Str.replace(/[^0-9A-Za-z]/g, \"\")(storedText(value) ?? \"\"),\n  );\n\nexport interface OtpEdit {\n  readonly value: string;\n  readonly focus: number;\n}\n\nexport const otpCells = (\n  value: unknown,\n  length: number,\n): ReadonlyArray<string> => {\n  const limit = otpLength(length);\n  const chars = Arr.fromIterable(normalizeOtp(value, limit));\n  return Arr.makeBy(limit, (index) =>\n    Option.getOrElse(Arr.get(chars, index), () => \"\"),\n  );\n};\n\nconst indexOf = (index: number, length: number): number =>\n  Math.min(\n    otpLength(length) - 1,\n    Math.max(0, Number.isFinite(index) ? Math.trunc(index) : 0),\n  );\n\nconst join = (cells: ReadonlyArray<string>): string => Arr.join(cells, \"\");\n\nconst withCell = (\n  cells: ReadonlyArray<string>,\n  index: number,\n  char: string,\n): ReadonlyArray<string> =>\n  Arr.map(cells, (cell, at) => (at === index ? char : cell));\n\nexport const applyOtpInput = (\n  current: unknown,\n  index: number,\n  text: string,\n  length: number,\n): OtpEdit => {\n  const limit = otpLength(length);\n  const cells = otpCells(current, limit);\n  const start = Math.min(indexOf(index, limit), Str.length(join(cells)));\n\n  const cell = Option.getOrElse(Arr.get(cells, start), () => \"\");\n  const edited =\n    cell !== \"\" &&\n    Str.length(text) > Str.length(cell) &&\n    Str.startsWith(cell)(text)\n      ? Str.slice(Str.length(cell))(text)\n      : text;\n  const typed = normalizeOtp(edited, limit);\n\n  if (typed === \"\")\n    return { value: join(withCell(cells, start, \"\")), focus: start };\n\n  if (Str.length(typed) === 1) {\n    return {\n      value: join(withCell(cells, start, typed)),\n      focus: Math.min(limit - 1, start + 1),\n    };\n  }\n\n  const inserted = Arr.fromIterable(typed);\n  const next = Arr.map(cells, (cell, at) =>\n    at >= start && at - start < Arr.length(inserted)\n      ? Option.getOrElse(Arr.get(inserted, at - start), () => cell)\n      : cell,\n  );\n  return {\n    value: join(next),\n    focus: Math.min(limit - 1, start + Arr.length(inserted)),\n  };\n};\n\nexport const otpEmptiedCell = (\n  before: unknown,\n  after: unknown,\n  index: number,\n  length: number,\n): boolean => {\n  const limit = otpLength(length);\n  const at = indexOf(index, limit);\n  const cell = (value: unknown): string =>\n    Option.getOrElse(Arr.get(otpCells(value, limit), at), () => \"\");\n  return cell(before) !== \"\" && cell(after) === \"\";\n};\n\nexport const decideOtpKey = (\n  current: unknown,\n  index: number,\n  key: string,\n  length: number,\n): Option.Option<OtpEdit> => {\n  const limit = otpLength(length);\n  const cells = otpCells(current, limit);\n  const at = indexOf(index, limit);\n  const value = join(cells);\n  const moveTo = (target: number): Option.Option<OtpEdit> =>\n    target === at ? Option.none() : Option.some({ value, focus: target });\n\n  if (key === \"left\") return moveTo(Math.max(0, at - 1));\n  if (key === \"right\") return moveTo(Math.min(limit - 1, at + 1));\n  if (key === \"home\") return moveTo(0);\n  if (key === \"end\") return moveTo(limit - 1);\n  if (key !== \"backspace\") return Option.none();\n\n  const filled = Option.getOrElse(Arr.get(cells, at), () => \"\") !== \"\";\n  if (filled || at === 0) return Option.none();\n  return Option.some({\n    value: join(withCell(cells, at - 1, \"\")),\n    focus: at - 1,\n  });\n};\n"
    },
    {
      "path": "packages/primitives/src/core/pointer.ts",
      "target": "@ui/gpuixui/primitives/core/pointer.ts",
      "type": "registry:lib",
      "content": "import { pointerWash } from \"../../tokens/index.js\";\nimport { colorRole, type PaintSource } from \"./theme.js\";\n\nexport const pressWash = (theme: PaintSource): string | undefined =>\n  pointerWash(theme, \"active\", \"hover\") ?? colorRole(theme, \"active\");\n\nexport const primaryButton = (event: { readonly button?: number }): boolean =>\n  event.button === undefined || event.button === 0;\n"
    },
    {
      "path": "packages/primitives/src/core/presence.ts",
      "target": "@ui/gpuixui/primitives/core/presence.ts",
      "type": "registry:lib",
      "content": "import type { MotionProps } from \"@gpuix/react\";\nimport {\n  exitWindowMs,\n  fadeFromAnchor,\n  presenceMotion,\n  presenceStyle,\n  presenceTiming,\n  type PresenceFrames,\n  type PresenceSide,\n  type PresenceTiming,\n} from \"../../motion/presence.js\";\n\nexport { exitWindowMs, fadeFromAnchor, presenceStyle };\n\nexport type AnchorSide = PresenceSide;\n\nexport type SurfaceFrames = PresenceFrames;\n\nexport type SurfaceTiming = PresenceTiming;\n\nexport const surfaceTiming = (reducedMotion = false): SurfaceTiming =>\n  presenceTiming(\"fast\", reducedMotion ? \"reduced\" : \"animated\");\n\nexport const surfaceMotion = (\n  frames: SurfaceFrames,\n  present: boolean,\n  timing: SurfaceTiming,\n): MotionProps => presenceMotion(frames, present, timing);\n"
    },
    {
      "path": "packages/primitives/src/core/rating.ts",
      "target": "@ui/gpuixui/primitives/core/rating.ts",
      "type": "registry:lib",
      "content": "import * as Option from \"effect/Option\";\n\nexport type StarState = \"checked\" | \"half\" | \"unchecked\";\n\nexport interface StarBox {\n  readonly x: number;\n  readonly width: number;\n}\n\nexport const starCount = (max: unknown): number =>\n  typeof max === \"number\" && Number.isFinite(max)\n    ? Math.max(1, Math.trunc(max))\n    : 1;\n\nconst snapRating = (value: number, half: boolean): number =>\n  half ? Math.round(value * 2) / 2 : Math.trunc(value);\n\nexport const clampRating = (\n  value: number,\n  max: number,\n  half = false,\n): number => {\n  if (!Number.isFinite(value)) return 0;\n  return Math.min(starCount(max), Math.max(0, snapRating(value, half)));\n};\n\nexport const starFill = (index: number, value: number): number =>\n  Math.min(1, Math.max(0, value - (index - 1)));\n\nexport const starState = (index: number, value: number): StarState => {\n  const fill = starFill(index, value);\n  return fill >= 1 ? \"checked\" : fill > 0 ? \"half\" : \"unchecked\";\n};\n\nconst ARROW_STEPS: Readonly<Record<string, number>> = {\n  right: 1,\n  up: 1,\n  left: -1,\n  down: -1,\n};\n\nexport const decideRating = (\n  value: number,\n  max: number,\n  key: string,\n  half = false,\n): Option.Option<number> => {\n  const limit = starCount(max);\n  const place = half ? 0.5 : 1;\n  if (key === \"home\") return Option.some(place);\n  if (key === \"end\") return Option.some(limit);\n  if (key === \"delete\" || key === \"backspace\") return Option.some(0);\n  const step = ARROW_STEPS[key];\n  if (step === undefined) return Option.none();\n  const current = clampRating(value, limit, half);\n  const lower = current === 0 ? 0 : place;\n  return Option.some(\n    Math.min(\n      limit,\n      Math.max(lower, clampRating(current + step * place, limit, half)),\n    ),\n  );\n};\n\nexport const ratingTabStop = (\n  value: number,\n  max: number,\n  half = false,\n): number => {\n  const current = clampRating(value, max, half);\n  return current === 0 ? 1 : Math.min(starCount(max), Math.ceil(current));\n};\n\nexport const pointerRating = (\n  star: number,\n  x: Option.Option<number>,\n  box: Option.Option<StarBox>,\n  half = false,\n): number => {\n  if (!half) return star;\n  return Option.match(x, {\n    onNone: () => star,\n    onSome: (pointer) =>\n      Option.match(box, {\n        onNone: () => star,\n        onSome: (target) =>\n          target.width > 0 && (pointer - target.x) / target.width < 0.5\n            ? Math.max(0.5, star - 0.5)\n            : star,\n      }),\n  });\n};\n"
    },
    {
      "path": "packages/primitives/src/core/roving.ts",
      "target": "@ui/gpuixui/primitives/core/roving.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\n\nexport const rovingIndex = (\n  count: number,\n  preferred: Option.Option<number>,\n  isDisabled: (index: number) => boolean,\n): Option.Option<number> => {\n  if (count <= 0) return Option.none();\n  const usable = (index: number): boolean =>\n    index >= 0 && index < count && !isDisabled(index);\n  return Option.match(Option.filter(preferred, usable), {\n    onSome: Option.some,\n    onNone: () => Arr.findFirst(Arr.range(0, count - 1), usable),\n  });\n};\n"
    },
    {
      "path": "packages/primitives/src/core/scroll.ts",
      "target": "@ui/gpuixui/primitives/core/scroll.ts",
      "type": "registry:lib",
      "content": "import { Match } from \"effect\";\nimport * as Option from \"effect/Option\";\nimport * as R from \"effect/Record\";\nimport { scrollAxis, type ScrollAxis } from \"./style.js\";\n\nexport interface ScrollOffset {\n  readonly x: number;\n  readonly y: number;\n}\n\nexport interface ScrollMetrics {\n  readonly line: number;\n\n  readonly page: { readonly width: number; readonly height: number };\n}\n\nexport interface ScrollRect {\n  readonly x: number;\n  readonly y: number;\n  readonly width: number;\n  readonly height: number;\n}\n\nexport const SCROLL_END = -1e7;\n\ninterface Move {\n  readonly x: -1 | 0 | 1;\n\n  readonly y: -1 | 0 | 1;\n  readonly size: \"line\" | \"page\" | \"end\" | \"start\";\n}\n\nconst MOVES: Record<string, Move> = {\n  up: { x: 0, y: 1, size: \"line\" },\n  down: { x: 0, y: -1, size: \"line\" },\n  pageup: { x: 0, y: 1, size: \"page\" },\n  pagedown: { x: 0, y: -1, size: \"page\" },\n  left: { x: 1, y: 0, size: \"line\" },\n  right: { x: -1, y: 0, size: \"line\" },\n  home: { x: 0, y: 0, size: \"start\" },\n  end: { x: -1, y: -1, size: \"end\" },\n};\n\nconst scrolls = (axis: ScrollAxis, side: \"x\" | \"y\"): boolean =>\n  axis === \"both\" || axis === (side === \"x\" ? \"horizontal\" : \"vertical\");\n\nconst pageAxis = (axis: ScrollAxis): Option.Option<\"x\" | \"y\"> =>\n  scrolls(axis, \"y\")\n    ? Option.some(\"y\")\n    : scrolls(axis, \"x\")\n      ? Option.some(\"x\")\n      : Option.none();\n\nconst next = (\n  current: number,\n  move: Move,\n  side: \"x\" | \"y\",\n  metrics: ScrollMetrics,\n): number => {\n  const direction =\n    move.size === \"page\" ? move.y : side === \"x\" ? move.x : move.y;\n  return Match.value(move.size).pipe(\n    Match.when(\"end\", () => SCROLL_END),\n    Match.when(\"start\", () => 0),\n    Match.when(\"page\", () =>\n      Math.min(\n        0,\n        current +\n          direction * (side === \"x\" ? metrics.page.width : metrics.page.height),\n      ),\n    ),\n    Match.when(\"line\", () => Math.min(0, current + direction * metrics.line)),\n    Match.exhaustive,\n  );\n};\n\nexport const scrollTarget = (\n  key: string,\n  axis: unknown,\n  current: ScrollOffset,\n  metrics: ScrollMetrics,\n): Option.Option<ScrollOffset> => {\n  const resolved = scrollAxis(axis);\n  return Option.flatMap(R.get(MOVES, key), (move) => {\n    const paged = move.size === \"page\" ? pageAxis(resolved) : Option.none();\n\n    const takes = (side: \"x\" | \"y\"): boolean =>\n      scrolls(resolved, side) &&\n      Match.value(move.size).pipe(\n        Match.when(\"start\", () => true),\n        Match.when(\"end\", () => true),\n        Match.when(\"page\", () => Option.contains(paged, side)),\n        Match.when(\"line\", () => (side === \"x\" ? move.x !== 0 : move.y !== 0)),\n        Match.exhaustive,\n      );\n    return takes(\"x\") || takes(\"y\")\n      ? Option.some({\n          x: takes(\"x\") ? next(current.x, move, \"x\", metrics) : current.x,\n          y: takes(\"y\") ? next(current.y, move, \"y\", metrics) : current.y,\n        })\n      : Option.none();\n  });\n};\n\nconst outside = (\n  row: ScrollRect,\n  viewport: ScrollRect,\n): { readonly above: number; readonly below: number } => ({\n  above: row.y - viewport.y,\n  below: row.y + row.height - (viewport.y + viewport.height),\n});\n\nexport const revealScroll = (\n  row: ScrollRect,\n  viewport: ScrollRect,\n  current: ScrollOffset,\n): Option.Option<ScrollOffset> => {\n  const { above, below } = outside(row, viewport);\n  const shift =\n    row.height > viewport.height\n      ? above\n      : above < 0\n        ? above\n        : below > 0\n          ? below\n          : 0;\n  return shift === 0\n    ? Option.none()\n    : Option.some({ x: current.x, y: current.y - shift });\n};\n"
    },
    {
      "path": "packages/primitives/src/core/selection.ts",
      "target": "@ui/gpuixui/primitives/core/selection.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport * as Schema from \"effect/Schema\";\nimport * as Str from \"effect/String\";\nimport { pipe } from \"effect\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport {\n  disabledOpacity,\n  elevationShadow,\n  filledHover,\n  filledPressed,\n  switchMetricsAt,\n  targetInset,\n  targetSize,\n} from \"../../tokens/index.js\";\nimport type { SwitchColors } from \"../../tokens/index.js\";\nimport {\n  iconSideOf,\n  borderWidthOf,\n  colorRole,\n  radiusValue,\n  spaceValue,\n} from \"./theme.js\";\nimport type { ControlSize, PrimitiveTheme } from \"./theme.js\";\nimport { pressWash } from \"./pointer.js\";\n\ntype SwitchSlot = \"on\" | \"off\" | \"knob\" | \"knobOff\" | \"edge\";\n\nconst DeclaredSwitch = Schema.Record(Schema.String, Schema.Unknown);\ntype DeclaredSwitch = typeof DeclaredSwitch.Type;\n\nconst decodeDeclaredSwitch = Schema.decodeUnknownOption(DeclaredSwitch);\n\nexport type SwitchPaintTheme = Omit<PrimitiveTheme, \"switch\"> & {\n  readonly switch?: unknown;\n};\n\nconst declaredPaint = (source: SwitchPaintTheme): Option.Option<SwitchColors> =>\n  Option.flatMap(Option.fromNullishOr(source.switch), (table) =>\n    Option.flatMap(decodeDeclaredSwitch(table), (paint) =>\n      Option.map(\n        Option.all({\n          on: declaredColour(paint, \"on\"),\n          off: declaredColour(paint, \"off\"),\n          knob: declaredColour(paint, \"knob\"),\n        }),\n        (slots) => {\n          const edge = Option.getOrUndefined(declaredColour(paint, \"edge\"));\n          const knobOff = Option.getOrUndefined(\n            declaredColour(paint, \"knobOff\"),\n          );\n          return {\n            ...slots,\n            ...(edge === undefined ? {} : { edge }),\n            ...(knobOff === undefined ? {} : { knobOff }),\n          };\n        },\n      ),\n    ),\n  );\n\nconst switchTheme = (source: SwitchPaintTheme): PrimitiveTheme => ({\n  ...source,\n  switch: Option.getOrUndefined(declaredPaint(source)),\n});\n\nconst declaredColour = (\n  paint: DeclaredSwitch,\n  slot: SwitchSlot,\n): Option.Option<string> =>\n  pipe(\n    Option.fromNullishOr(paint[slot]),\n    Option.filter(P.isString),\n    Option.filter(Str.isNonEmpty),\n  );\n\nexport const SELECTION_STATES = [\n  \"unchecked\",\n  \"checked\",\n  \"indeterminate\",\n] as const;\nexport type SelectionState = (typeof SELECTION_STATES)[number];\n\nexport const SELECTION_ACCENTS = [\"primary\", \"destructive\"] as const;\nexport type SelectionAccent = (typeof SELECTION_ACCENTS)[number];\n\nconst ACCENT_NAMES: ReadonlyArray<string> = SELECTION_ACCENTS;\n\nexport const isSelectionAccent = (value: unknown): value is SelectionAccent =>\n  typeof value === \"string\" && Arr.contains(ACCENT_NAMES, value);\n\nexport const selectionAccentOf = (value: unknown): SelectionAccent =>\n  isSelectionAccent(value) ? value : \"primary\";\n\nexport interface SelectionSpec {\n  readonly state: SelectionState;\n  readonly size: ControlSize;\n  readonly accent: SelectionAccent;\n\n  readonly shape?: \"square\" | \"circle\";\n  readonly invalid?: boolean;\n  readonly disabled?: boolean;\n}\n\nconst filled = (spec: SelectionSpec): boolean => spec.state !== \"unchecked\";\n\nconst accentRole = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): {\n  readonly background: string | undefined;\n  readonly foreground: string | undefined;\n} =>\n  spec.accent === \"destructive\"\n    ? {\n        background: colorRole(theme, \"destructive\"),\n        foreground: colorRole(theme, \"destructiveForeground\"),\n      }\n    : {\n        background: colorRole(theme, \"primary\"),\n        foreground: colorRole(theme, \"primaryForeground\"),\n      };\n\nexport const selectionMarkColor = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): string | undefined =>\n  filled(spec)\n    ? accentRole(spec, theme).foreground\n    : spec.invalid === true\n      ? colorRole(theme, \"destructive\")\n      : colorRole(theme, \"mutedForeground\");\n\nconst feedbackFill = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n  phase: \"hover\" | \"press\",\n): StyleDesc => ({\n  backgroundColor:\n    phase === \"hover\"\n      ? filledHover(theme, selectionAccentOf(spec.accent))\n      : filledPressed(theme, selectionAccentOf(spec.accent)),\n  borderColor: \"transparent\",\n});\n\nexport const selectionBoxStyle = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const side = iconSideOf(theme, spec.size);\n  const role = accentRole(spec, theme);\n  const emptyBorder =\n    spec.invalid === true\n      ? colorRole(theme, \"destructive\")\n      : colorRole(theme, \"borderStrong\");\n  const style: StyleDesc = {\n    display: \"flex\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    width: side,\n    height: side,\n    flexShrink: 0,\n    borderRadius:\n      spec.shape === \"circle\"\n        ? radiusValue(theme, \"full\")\n        : radiusValue(theme, \"sm\"),\n    borderWidth: borderWidthOf(theme, \"thin\"),\n    borderColor: filled(spec) ? role.background : emptyBorder,\n    backgroundColor: filled(spec) ? role.background : undefined,\n    color: selectionMarkColor(spec, theme),\n    cursor: spec.disabled === true ? \"not-allowed\" : \"pointer\",\n    hover: filled(spec)\n      ? feedbackFill(spec, theme, \"hover\")\n      : { backgroundColor: colorRole(theme, \"hover\") },\n    active: filled(spec)\n      ? feedbackFill(spec, theme, \"press\")\n      : { backgroundColor: pressWash(theme) },\n  };\n  if (spec.disabled === true) {\n    style.opacity = disabledOpacity;\n\n    style.hover = {\n      backgroundColor: filled(spec) ? role.background : undefined,\n      borderColor: filled(spec) ? role.background : emptyBorder,\n    };\n    style.active = {};\n  }\n  return style;\n};\n\nexport const switchThumbInset = (\n  spec: SelectionSpec,\n  source: SwitchPaintTheme,\n): number => switchBox(spec.size, source).inset;\n\nexport interface SwitchBox {\n  readonly width: number;\n\n  readonly height: number;\n\n  readonly knob: number;\n\n  readonly inset: number;\n\n  readonly border: number;\n}\n\nexport const switchBox = (\n  size: ControlSize,\n  source: SwitchPaintTheme,\n): SwitchBox => {\n  const theme = switchTheme(source);\n\n  const metrics = Option.getOrUndefined(\n    Option.fromNullishOr(theme.switchMetrics),\n  );\n  if (theme.switch !== undefined && metrics !== undefined) {\n    return switchMetricsAt(metrics, size);\n  }\n  const iconSide = iconSideOf(theme, size);\n  const inset = spaceValue(theme, \"2xs\");\n  return {\n    width: Math.round(iconSide * 1.75),\n    height: iconSide,\n    knob: iconSide - inset * 2,\n    inset,\n    border: borderWidthOf(theme, \"thin\"),\n  };\n};\n\nexport const selectionTargetStyle = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const side = iconSideOf(theme, spec.size);\n  const inset = targetInset(side);\n  return {\n    display: \"flex\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    width: side + inset * 2,\n    height: side + inset * 2,\n    flexShrink: 0,\n    cursor: spec.disabled === true ? \"not-allowed\" : \"pointer\",\n  };\n};\n\nexport const switchTargetStyle = (spec: SelectionSpec): StyleDesc => ({\n  display: \"flex\",\n  alignItems: \"center\",\n  justifyContent: \"center\",\n  minHeight: targetSize.minimum,\n  flexShrink: 0,\n  cursor: spec.disabled === true ? \"not-allowed\" : \"pointer\",\n});\n\nexport const switchThumbTravel = (\n  spec: SelectionSpec,\n  theme: SwitchPaintTheme,\n): number => {\n  const box = switchBox(spec.size, theme);\n  return box.width - box.knob - box.inset * 2;\n};\n\nexport const switchThumbOffset = (\n  spec: SelectionSpec,\n  theme: SwitchPaintTheme,\n): number => {\n  const box = switchBox(spec.size, theme);\n  const rest = box.inset - box.border;\n  return rest + (filled(spec) ? switchThumbTravel(spec, theme) : 0);\n};\n\nconst switchFill = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): string | undefined =>\n  theme.switch === undefined\n    ? undefined\n    : filled(spec)\n      ? theme.switch.on\n      : spec.invalid === true\n        ? colorRole(theme, \"destructive\")\n        : theme.switch.off;\n\nexport const switchTrackPaint = (\n  spec: SelectionSpec,\n  source: SwitchPaintTheme,\n): StyleDesc | undefined => {\n  const theme = switchTheme(source);\n  const fill = switchFill(spec, theme);\n  if (fill === undefined) return undefined;\n  const edge = filled(spec)\n    ? fill\n    : spec.invalid === true\n      ? colorRole(theme, \"destructive\")\n      : (theme.switch?.edge ?? fill);\n  const box = switchBox(spec.size, source);\n  return {\n    backgroundColor: fill,\n    borderColor: edge,\n\n    boxShadow:\n      !filled(spec) && box.border === 0 && theme.switch?.edge !== undefined\n        ? {\n            offsetX: 0,\n            offsetY: 0,\n            blurRadius: 0,\n            spreadRadius: 1,\n            color: edge ?? fill,\n          }\n        : undefined,\n    hover: { backgroundColor: fill, borderColor: edge },\n    active: { backgroundColor: fill, borderColor: edge },\n  };\n};\n\nexport const switchThumbPaint = (\n  spec: SelectionSpec,\n  source: SwitchPaintTheme,\n): StyleDesc | undefined => {\n  const theme = switchTheme(source);\n  return theme.switch === undefined\n    ? undefined\n    : {\n        backgroundColor: filled(spec)\n          ? theme.switch.knob\n          : (theme.switch.knobOff ?? theme.switch.knob),\n        boxShadow: elevationShadow(theme, \"sm\"),\n      };\n};\n\nexport const switchTrackStyle = (\n  spec: SelectionSpec,\n  source: SwitchPaintTheme,\n): StyleDesc => {\n  const theme = switchTheme(source);\n  const box = switchBox(spec.size, source);\n  const role = accentRole(spec, theme);\n  const emptyBorder =\n    spec.invalid === true\n      ? colorRole(theme, \"destructive\")\n      : colorRole(theme, \"borderStrong\");\n  const paint = switchTrackPaint(spec, source);\n  const fill =\n    paint?.backgroundColor ??\n    (filled(spec) ? role.background : colorRole(theme, \"muted\"));\n  const edge =\n    paint?.borderColor ?? (filled(spec) ? role.background : emptyBorder);\n  const style: StyleDesc = {\n    display: \"flex\",\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    position: \"relative\",\n    width: box.width,\n    height: box.height,\n    flexShrink: 0,\n    borderRadius: radiusValue(theme, \"full\"),\n    borderWidth: box.border,\n    borderColor: edge,\n    backgroundColor: fill,\n    boxShadow: paint?.boxShadow,\n    cursor: spec.disabled === true ? \"not-allowed\" : \"pointer\",\n    hover:\n      paint?.hover ??\n      (filled(spec)\n        ? feedbackFill(spec, theme, \"hover\")\n        : { backgroundColor: colorRole(theme, \"hover\") }),\n    active:\n      paint?.active ??\n      (filled(spec)\n        ? feedbackFill(spec, theme, \"press\")\n        : { backgroundColor: pressWash(theme) }),\n  };\n  if (spec.disabled === true) {\n    style.opacity = disabledOpacity;\n    style.hover = { backgroundColor: fill, borderColor: edge };\n    style.active = {};\n  }\n  return style;\n};\n\nexport const switchThumbStyle = (\n  spec: SelectionSpec,\n  source: SwitchPaintTheme,\n): StyleDesc => {\n  const theme = switchTheme(source);\n  const paint = switchThumbPaint(spec, source);\n  const box = switchBox(spec.size, source);\n  return {\n    position: \"absolute\",\n\n    top: (box.height - box.border * 2 - box.knob) / 2,\n    left: switchThumbOffset(spec, source),\n    width: box.knob,\n    height: box.knob,\n    flexShrink: 0,\n    borderRadius: radiusValue(theme, \"full\"),\n    pointerEvents: \"none\",\n    backgroundColor:\n      paint?.backgroundColor ??\n      (filled(spec)\n        ? accentRole(spec, theme).foreground\n        : colorRole(theme, \"foreground\")),\n    boxShadow: paint?.boxShadow,\n  };\n};\n\nexport const radioDotStyle = (\n  spec: SelectionSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const side = Math.round(iconSideOf(theme, spec.size) / 2);\n  return {\n    width: side,\n    height: side,\n    flexShrink: 0,\n    borderRadius: radiusValue(theme, \"full\"),\n    backgroundColor: selectionMarkColor(spec, theme),\n  };\n};\n\nexport const toggleSelection = (\n  mode: \"single\" | \"multiple\",\n  current: ReadonlyArray<string>,\n  value: string,\n): ReadonlyArray<string> =>\n  mode === \"single\"\n    ? Arr.contains(current, value)\n      ? []\n      : [value]\n    : Arr.contains(current, value)\n      ? Arr.filter(current, (item) => item !== value)\n      : [...current, value];\n"
    },
    {
      "path": "packages/primitives/src/core/state.ts",
      "target": "@ui/gpuixui/primitives/core/state.ts",
      "type": "registry:lib",
      "content": "import * as Option from \"effect/Option\";\n\nexport type ControlMode = \"controlled\" | \"uncontrolled\";\n\nexport interface ValueState<Value> {\n  readonly mode: ControlMode;\n\n  readonly current: Value;\n\n  readonly internal: Value;\n}\n\nexport const initValueState = <Value>(\n  value: Option.Option<Value>,\n  defaultValue: Value,\n): ValueState<Value> => ({\n  mode: Option.isSome(value) ? \"controlled\" : \"uncontrolled\",\n  current: Option.getOrElse(value, () => defaultValue),\n  internal: defaultValue,\n});\n\nexport const setValue = <Value>(\n  state: ValueState<Value>,\n  next: Value,\n): { readonly state: ValueState<Value>; readonly notify: boolean } => {\n  const notify = !Object.is(state.current, next);\n  if (state.mode === \"controlled\") {\n    return {\n      state: { mode: state.mode, current: next, internal: state.internal },\n      notify,\n    };\n  }\n  return {\n    state: { mode: state.mode, current: next, internal: next },\n    notify,\n  };\n};\n\nexport const withValue = <Value>(\n  state: ValueState<Value>,\n  value: Option.Option<Value>,\n): ValueState<Value> =>\n  Option.match(value, {\n    onSome: (incoming) => ({\n      mode: \"controlled\",\n      current: incoming,\n      internal: state.internal,\n    }),\n    onNone: () => state,\n  });\n"
    },
    {
      "path": "packages/primitives/src/core/stored.ts",
      "target": "@ui/gpuixui/primitives/core/stored.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\n\nexport const storedText = (value: unknown): string | undefined =>\n  P.isString(value) ? value : undefined;\n\nexport const storedList = <A>(\n  value: unknown,\n  read: (entry: unknown) => Option.Option<A>,\n): ReadonlyArray<A> =>\n  Arr.isArray(value) ? Arr.getSomes(Arr.map(value, read)) : [];\n\nexport const storedTextList = (\n  value: unknown,\n): Option.Option<ReadonlyArray<string>> =>\n  Arr.isArray(value)\n    ? Option.some(Arr.filter(value, P.isString))\n    : Option.none();\n\nexport const storedNumber = (value: unknown): Option.Option<number> =>\n  typeof value === \"number\" ? Option.some(value) : Option.none();\n"
    },
    {
      "path": "packages/primitives/src/core/style.ts",
      "target": "@ui/gpuixui/primitives/core/style.ts",
      "type": "registry:lib",
      "content": "import type { StyleDesc } from \"@gpuix/react\";\nimport {\n  brandText,\n  disabledAction,\n  disabledOpacity,\n  elevationShadow,\n  targetSize,\n  type FilledAction,\n} from \"../../tokens/index.js\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport type {\n  ColorRef,\n  ControlSize,\n  ElevationLevel,\n  PrimitiveTheme,\n  RadiusStep,\n  SpaceStep,\n  TextRole,\n} from \"./theme.js\";\nimport { controlRing } from \"./focus.js\";\nimport { type Orientation, orientationOf } from \"./keyboard.js\";\nimport { pressWash } from \"./pointer.js\";\nimport {\n  borderWidthOf,\n  colorRole,\n  controlHeightOf,\n  controlPaddingOf,\n  familyOf,\n  iconSideOf,\n  radiusStep,\n  radiusValue,\n  spaceValue,\n  typeOf,\n} from \"./theme.js\";\nimport { fieldVariant } from \"./variants.js\";\n\ntype Paint = ColorRef | undefined;\n\nconst paint = (ref: Paint, theme: PrimitiveTheme): string | undefined =>\n  Option.getOrUndefined(\n    Option.map(Option.fromNullishOr(ref), (colorRef) => colorRef(theme)),\n  );\n\nexport interface ControlVariant {\n  readonly background: Paint;\n  readonly foreground: ColorRef;\n  readonly border: Paint;\n\n  readonly filledAction?: FilledAction;\n\n  readonly hover: (theme: PrimitiveTheme) => StyleDesc;\n  readonly active: (theme: PrimitiveTheme) => StyleDesc;\n}\n\nconst CONTROL_GAP: SpaceStep = \"sm\";\n\nexport interface ControlSpec {\n  readonly variant: ControlVariant;\n  readonly size: ControlSize;\n\n  readonly square?: boolean;\n  readonly fullWidth?: boolean;\n  readonly disabled?: boolean;\n\n  readonly invalid?: boolean;\n}\n\nexport const controlStyle = (\n  spec: ControlSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const { variant, size } = spec;\n  const height = controlHeightOf(theme, size);\n  const padding = controlPaddingOf(theme, size);\n  const label = typeOf(theme, \"label\");\n  const rest = paint(variant.background, theme);\n  const border =\n    spec.invalid === true\n      ? colorRole(theme, \"destructive\")\n      : paint(variant.border, theme);\n\n  const style: StyleDesc = {\n    display: \"flex\",\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    gap: spaceValue(theme, CONTROL_GAP),\n    height,\n    borderRadius: radiusValue(theme, \"md\"),\n    fontSize: label.fontSize,\n    lineHeight: label.lineHeight,\n    fontWeight: label.fontWeight,\n    backgroundColor: rest,\n    color: paint(variant.foreground, theme),\n    borderWidth:\n      variant.border === undefined ? 0 : borderWidthOf(theme, \"thin\"),\n    borderColor: border,\n    paddingLeft: padding,\n    paddingRight: padding,\n    cursor: spec.disabled === true ? \"not-allowed\" : \"pointer\",\n  };\n\n  if (spec.disabled === true) {\n    const pair =\n      variant.filledAction === undefined ? undefined : disabledAction(theme);\n    if (pair === undefined) {\n      style.opacity = disabledOpacity;\n    } else {\n      style.backgroundColor = pair.backgroundColor;\n      style.color = pair.color;\n    }\n    style.hover = { backgroundColor: style.backgroundColor };\n    style.active = { backgroundColor: style.backgroundColor };\n  } else {\n    if (theme.interaction !== \"appkit\") style.hover = variant.hover(theme);\n    style.active = variant.active(theme);\n  }\n  if (spec.square === true) {\n    style.width = height;\n    style.paddingLeft = 0;\n    style.paddingRight = 0;\n  }\n  if (spec.fullWidth === true) style.width = \"100%\";\n  return style;\n};\n\nexport const controlFocusStyle = (\n  spec: ControlSpec,\n  theme: PrimitiveTheme,\n): StyleDesc =>\n  controlRing(theme, { disabled: spec.disabled, invalid: spec.invalid });\n\nexport const fieldTextStyle = (theme: PrimitiveTheme): StyleDesc => {\n  const body = typeOf(theme, \"body\");\n  return {\n    justifyContent: \"flex-start\",\n    fontSize: body.fontSize,\n    lineHeight: body.lineHeight,\n    fontWeight: body.fontWeight,\n    cursor: \"text\",\n    userSelect: \"text\",\n    selectionColor: colorRole(theme, \"selection\"),\n  };\n};\n\nexport {\n  nativeTextFills,\n  nativeTextTheme,\n  type NativeTextFill,\n  type NativeTextTheme,\n} from \"../../tokens/index.js\";\n\nexport const TEXT_TONES = [\n  \"default\",\n  \"muted\",\n  \"primary\",\n  \"destructive\",\n  \"inherit\",\n] as const;\n\nexport type TextTone = (typeof TEXT_TONES)[number];\n\nconst TEXT_TONE_NAMES: ReadonlyArray<string> = TEXT_TONES;\n\nexport const isTextTone = (value: unknown): value is TextTone =>\n  typeof value === \"string\" && Arr.contains(TEXT_TONE_NAMES, value);\n\nexport const textToneOf = (value: unknown): TextTone =>\n  isTextTone(value) ? value : \"default\";\n\nconst tones: Record<TextTone, Paint> = {\n  default: (theme) => colorRole(theme, \"foreground\"),\n  muted: (theme) => colorRole(theme, \"mutedForeground\"),\n\n  primary: brandText,\n\n  destructive: (theme) => colorRole(theme, \"destructiveSoftForeground\"),\n\n  inherit: undefined,\n};\n\nexport const toneColor = (\n  theme: PrimitiveTheme,\n  tone: TextTone,\n): string | undefined => paint(tones[textToneOf(tone)], theme);\n\nexport const TEXT_ALIGNMENTS = [\"start\", \"center\", \"end\"] as const;\nexport type TextAlign = (typeof TEXT_ALIGNMENTS)[number];\n\nconst ALIGN_VALUES: Readonly<Record<TextAlign, \"left\" | \"center\" | \"right\">> = {\n  start: \"left\",\n  center: \"center\",\n  end: \"right\",\n};\n\nexport const textAlignValue = (align: TextAlign): \"left\" | \"center\" | \"right\" =>\n  ALIGN_VALUES[align];\n\nexport const isTextAlign = (align: unknown): align is TextAlign =>\n  P.isString(align) && Arr.contains(TEXT_ALIGNMENTS, align);\n\nexport const textAlignOf = (align: unknown): TextAlign | undefined =>\n  isTextAlign(align) ? align : undefined;\n\nexport interface TextSpec {\n  readonly role: TextRole;\n  readonly tone: TextTone;\n  readonly align?: TextAlign;\n  readonly truncate?: boolean;\n\n  readonly clamp?: number;\n\n  readonly selectable?: boolean;\n}\n\nconst MONO_ROLES: ReadonlyArray<TextRole> = [\"code\", \"codeBlock\"];\n\nexport const clampLines = (clamp: unknown): number =>\n  typeof clamp === \"number\" && Number.isFinite(clamp)\n    ? Math.max(1, Math.floor(clamp))\n    : 1;\n\nexport const lineClampOf = (clamp: unknown): number | undefined =>\n  Option.match(Option.fromNullishOr(clamp), {\n    onNone: () => undefined,\n    onSome: (count) => clampLines(count),\n  });\n\nexport const textStyle = (spec: TextSpec, theme: PrimitiveTheme): StyleDesc => {\n  const role = typeOf(theme, spec.role);\n  const style: StyleDesc = {\n    fontSize: role.fontSize,\n    lineHeight: role.lineHeight,\n    fontWeight: role.fontWeight,\n    fontFamily: Arr.contains(MONO_ROLES, spec.role)\n      ? familyOf(theme, \"mono\")\n      : familyOf(theme, \"sans\"),\n    color: toneColor(theme, spec.tone),\n    userSelect: spec.selectable === true ? \"text\" : \"none\",\n  };\n  const align = textAlignOf(spec.align);\n  if (align !== undefined) style.textAlign = textAlignValue(align);\n\n  if (spec.selectable === true)\n    style.selectionColor = colorRole(theme, \"selection\");\n  if (spec.truncate === true) {\n    style.whiteSpace = \"nowrap\";\n    style.textOverflow = \"ellipsis\";\n  }\n  const clamp = lineClampOf(spec.clamp);\n  if (clamp !== undefined) {\n    style.whiteSpace = \"normal\";\n    style.height = role.lineHeight * clamp;\n    style.overflow = \"hidden\";\n  }\n  return style;\n};\n\nexport const SURFACE_TONES = [\n  \"none\",\n  \"background\",\n  \"card\",\n  \"popover\",\n  \"muted\",\n  \"accent\",\n  \"selected\",\n  \"skeleton\",\n] as const;\n\nexport type SurfaceTone = (typeof SURFACE_TONES)[number];\n\nconst surfaces: Record<SurfaceTone, Paint> = {\n  none: undefined,\n  background: (theme) => colorRole(theme, \"background\"),\n  card: (theme) => colorRole(theme, \"card\"),\n  popover: (theme) => colorRole(theme, \"popover\"),\n  muted: (theme) => colorRole(theme, \"muted\"),\n  accent: (theme) => colorRole(theme, \"accent\"),\n  selected: (theme) => colorRole(theme, \"selected\"),\n  skeleton: (theme) => colorRole(theme, \"skeleton\"),\n};\n\nexport const BORDER_STYLES = [\"solid\", \"dashed\"] as const;\nexport type BorderStyle = (typeof BORDER_STYLES)[number];\n\nexport interface SurfaceSpec {\n  readonly tone: SurfaceTone;\n\n  readonly padding?: SpaceStep;\n\n  readonly paddingX?: SpaceStep;\n\n  readonly paddingY?: SpaceStep;\n  readonly radius?: RadiusStep;\n  readonly border?: boolean;\n\n  readonly borderStyle?: BorderStyle;\n\n  readonly aspectRatio?: number;\n  readonly elevation?: ElevationLevel;\n}\n\nexport interface PaddingSides {\n  readonly top?: SpaceStep;\n  readonly right?: SpaceStep;\n  readonly bottom?: SpaceStep;\n  readonly left?: SpaceStep;\n}\n\nexport const paddingSides = ({\n  padding,\n  paddingX,\n  paddingY,\n}: Pick<SurfaceSpec, \"padding\" | \"paddingX\" | \"paddingY\">): PaddingSides =>\n  paddingX === undefined && paddingY === undefined\n    ? { top: padding, right: padding, bottom: padding, left: padding }\n    : {\n        top: paddingY ?? padding,\n        right: paddingX ?? padding,\n        bottom: paddingY ?? padding,\n        left: paddingX ?? padding,\n      };\n\nexport const paintableAspectRatio = (\n  aspectRatio: number | undefined,\n): number | undefined =>\n  aspectRatio !== undefined && Number.isFinite(aspectRatio) && aspectRatio > 0\n    ? aspectRatio\n    : undefined;\n\nexport const surfaceStyle = (\n  spec: SurfaceSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const style: StyleDesc = {\n    display: \"flex\",\n    flexDirection: \"column\",\n    backgroundColor: paint(surfaces[spec.tone], theme),\n  };\n  const sides = paddingSides(spec);\n  if (spec.paddingX === undefined && spec.paddingY === undefined) {\n    if (sides.top !== undefined) style.padding = spaceValue(theme, sides.top);\n  } else {\n    if (sides.top !== undefined)\n      style.paddingTop = spaceValue(theme, sides.top);\n    if (sides.right !== undefined)\n      style.paddingRight = spaceValue(theme, sides.right);\n    if (sides.bottom !== undefined)\n      style.paddingBottom = spaceValue(theme, sides.bottom);\n    if (sides.left !== undefined)\n      style.paddingLeft = spaceValue(theme, sides.left);\n  }\n  const corner = radiusStep(spec.radius);\n  if (corner !== undefined) style.borderRadius = radiusValue(theme, corner);\n  if (spec.border === true) {\n    style.borderWidth = borderWidthOf(theme, \"thin\");\n    style.borderColor = colorRole(theme, \"border\");\n    if (spec.borderStyle === \"dashed\") style.borderStyle = \"dashed\";\n  }\n  Option.map(\n    Option.fromNullishOr(paintableAspectRatio(spec.aspectRatio)),\n    (ratio) => {\n      style.aspectRatio = ratio;\n    },\n  );\n  if (spec.elevation !== undefined) {\n    Option.map(\n      Option.fromNullishOr(elevationShadow(theme, spec.elevation)),\n      (shadow) => {\n        style.boxShadow = shadow;\n      },\n    );\n  }\n  return style;\n};\n\nexport interface RuleSpec {\n  readonly orientation: Orientation;\n\n  readonly inset?: SpaceStep;\n}\n\nexport const ruleStyle = (spec: RuleSpec, theme: PrimitiveTheme): StyleDesc => {\n  const inset = spec.inset === undefined ? 0 : spaceValue(theme, spec.inset);\n  return orientationOf(spec.orientation, \"horizontal\") === \"horizontal\"\n    ? {\n        height: borderWidthOf(theme, \"thin\"),\n        flexShrink: 0,\n        backgroundColor: colorRole(theme, \"border\"),\n        marginLeft: inset,\n        marginRight: inset,\n      }\n    : {\n        width: borderWidthOf(theme, \"thin\"),\n        flexShrink: 0,\n        backgroundColor: colorRole(theme, \"border\"),\n        marginTop: inset,\n        marginBottom: inset,\n      };\n};\n\nexport const SCROLL_AXES = [\"vertical\", \"horizontal\", \"both\"] as const;\n\nexport type ScrollAxis = (typeof SCROLL_AXES)[number];\n\nconst SCROLL_AXIS_NAMES: ReadonlyArray<string> = SCROLL_AXES;\n\nexport const isScrollAxis = (value: unknown): value is ScrollAxis =>\n  P.isString(value) && Arr.contains(SCROLL_AXIS_NAMES, value);\n\nexport const scrollAxis = (value: unknown): ScrollAxis =>\n  isScrollAxis(value) ? value : \"vertical\";\n\nexport interface ScrollSpec {\n  readonly axis: ScrollAxis;\n}\n\nexport const scrollStyle = (spec: ScrollSpec): StyleDesc => {\n  const axis = scrollAxis(spec.axis);\n  return {\n    overflowY: axis === \"horizontal\" ? \"hidden\" : \"scroll\",\n    overflowX: axis === \"vertical\" ? \"hidden\" : \"scroll\",\n  };\n};\n\nexport const progressPercent = (value: unknown): number =>\n  typeof value === \"number\" && !Number.isNaN(value)\n    ? Math.min(100, Math.max(0, value))\n    : 0;\n\nexport const PROGRESS_TONES = [\n  \"primary\",\n  \"success\",\n  \"destructive\",\n  \"muted\",\n] as const;\n\nexport type ProgressTone = (typeof PROGRESS_TONES)[number];\n\nconst PROGRESS_TONE_NAMES: ReadonlyArray<string> = PROGRESS_TONES;\n\nconst progressTones: Record<ProgressTone, ColorRef> = {\n  primary: brandText,\n  success: (theme) => colorRole(theme, \"success\"),\n  destructive: (theme) => colorRole(theme, \"destructive\"),\n  muted: (theme) => colorRole(theme, \"mutedForeground\"),\n};\n\nexport const PROGRESS_SIZES = [\"sm\", \"md\"] as const;\nexport type ProgressSize = (typeof PROGRESS_SIZES)[number];\n\nconst PROGRESS_SIZE_NAMES: ReadonlyArray<string> = PROGRESS_SIZES;\n\nexport const isProgressSize = (value: unknown): value is ProgressSize =>\n  typeof value === \"string\" && Arr.contains(PROGRESS_SIZE_NAMES, value);\n\nexport const progressSizeOf = (value: unknown): ProgressSize =>\n  isProgressSize(value) ? value : \"md\";\n\nexport const isProgressTone = (value: unknown): value is ProgressTone =>\n  typeof value === \"string\" && Arr.contains(PROGRESS_TONE_NAMES, value);\n\nexport const progressToneOf = (value: unknown): ProgressTone =>\n  isProgressTone(value) ? value : \"primary\";\n\nexport interface ProgressSpec {\n  readonly value?: number;\n  readonly tone: ProgressTone;\n  readonly size: ProgressSize;\n}\n\nconst PROGRESS_HEIGHT: Record<ProgressSize, SpaceStep> = {\n  sm: \"xs\",\n  md: \"sm\",\n};\n\nexport const progressTrackStyle = (\n  spec: ProgressSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => ({\n  display: \"flex\",\n  flexDirection: \"row\",\n  width: \"100%\",\n  height: spaceValue(theme, PROGRESS_HEIGHT[progressSizeOf(spec.size)]),\n  borderRadius: radiusValue(theme, \"full\"),\n  backgroundColor: colorRole(theme, \"muted\"),\n  overflow: \"hidden\",\n});\n\nexport const progressFillStyle = (\n  spec: ProgressSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => ({\n  height: \"100%\",\n  width: `${progressPercent(spec.value ?? 0)}%`,\n  borderRadius: radiusValue(theme, \"full\"),\n  backgroundColor: paint(progressTones[progressToneOf(spec.tone)], theme),\n});\n\nexport const SLIDER_ORIENTATIONS = [\"horizontal\", \"vertical\"] as const;\nexport type SliderOrientation = (typeof SLIDER_ORIENTATIONS)[number];\n\nexport interface SliderSpec {\n  readonly size: ControlSize;\n  readonly invalid?: boolean;\n  readonly disabled?: boolean;\n\n  readonly orientation?: SliderOrientation;\n}\n\nconst isVertical = (spec: Pick<SliderSpec, \"orientation\">): boolean =>\n  spec.orientation === \"vertical\";\n\nexport const sliderInset = (size: ControlSize, theme: PrimitiveTheme): number =>\n  Math.round(iconSideOf(theme, size) / 2);\n\nexport const sliderTrackStyle = (\n  spec: SliderSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const inset = sliderInset(spec.size, theme);\n  const control = controlHeightOf(theme, spec.size);\n  const style: StyleDesc = isVertical(spec)\n    ? {\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"center\",\n        width: control,\n        height: \"100%\",\n        paddingTop: inset,\n        paddingBottom: inset,\n      }\n    : {\n        display: \"flex\",\n        flexDirection: \"row\",\n        alignItems: \"center\",\n        width: \"100%\",\n        height: control,\n        paddingLeft: inset,\n        paddingRight: inset,\n      };\n  style.cursor = spec.disabled === true ? \"not-allowed\" : \"pointer\";\n  if (spec.disabled === true) style.opacity = disabledOpacity;\n  return style;\n};\n\nexport const sliderRailStyle = (\n  spec: SliderSpec,\n  theme: PrimitiveTheme,\n): StyleDesc =>\n  isVertical(spec)\n    ? {\n        width: spaceValue(theme, \"xs\"),\n        height: \"100%\",\n        borderRadius: radiusValue(theme, \"full\"),\n        backgroundColor: colorRole(theme, \"muted\"),\n        display: \"flex\",\n\n        flexDirection: \"column\",\n        justifyContent: \"flex-end\",\n        alignItems: \"center\",\n        pointerEvents: \"none\",\n      }\n    : {\n        width: \"100%\",\n        height: spaceValue(theme, \"xs\"),\n        borderRadius: radiusValue(theme, \"full\"),\n        backgroundColor: colorRole(theme, \"muted\"),\n        display: \"flex\",\n        flexDirection: \"row\",\n        alignItems: \"center\",\n        pointerEvents: \"none\",\n      };\n\nexport const sliderFillStyle = (\n  fraction: number,\n  spec: SliderSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const share = `${(Number.isFinite(fraction) ? Math.min(1, Math.max(0, fraction)) : 0) * 100}%`;\n  const style: StyleDesc = isVertical(spec)\n    ? { width: \"100%\", height: share }\n    : { width: share, height: \"100%\" };\n  style.flexShrink = 0;\n  style.borderRadius = radiusValue(theme, \"full\");\n  style.backgroundColor =\n    spec.invalid === true\n      ? colorRole(theme, \"destructive\")\n      : colorRole(theme, \"primary\");\n  style.pointerEvents = \"none\";\n  return style;\n};\n\nexport const sliderThumbStyle = (\n  spec: SliderSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const side = iconSideOf(theme, spec.size);\n  const half = Math.round(side / 2);\n  const style: StyleDesc = {\n    width: side,\n    height: side,\n    flexShrink: 0,\n    borderRadius: radiusValue(theme, \"full\"),\n    backgroundColor: colorRole(theme, \"input\"),\n    borderWidth: borderWidthOf(theme, \"strong\"),\n    borderColor:\n      spec.invalid === true\n        ? colorRole(theme, \"destructive\")\n        : colorRole(theme, \"primary\"),\n    ...(isVertical(spec)\n      ? { marginTop: -half, marginBottom: -half }\n      : { marginLeft: -half, marginRight: -half }),\n    pointerEvents: \"none\",\n  };\n  return style;\n};\n\nexport const numberInputStyle = (): StyleDesc => ({\n  flexGrow: 1,\n  height: \"100%\",\n  minWidth: 0,\n  paddingLeft: 0,\n  paddingRight: 0,\n  borderWidth: 0,\n});\n\nexport const numberStepperColumnStyle = (theme: PrimitiveTheme): StyleDesc => ({\n  display: \"flex\",\n  flexDirection: \"column\",\n  width: iconSideOf(theme, \"md\") + spaceValue(theme, \"sm\"),\n  height: \"100%\",\n  flexShrink: 0,\n});\n\nexport const numberStepperStyle = (\n  spec: { readonly disabled?: boolean },\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const style: StyleDesc = {\n    display: \"flex\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    flexGrow: 1,\n    borderRadius: radiusValue(theme, \"sm\"),\n    cursor: spec.disabled === true ? \"not-allowed\" : \"pointer\",\n  };\n  if (spec.disabled !== true) {\n    style.hover = { backgroundColor: colorRole(theme, \"hover\") };\n\n    style.active = { backgroundColor: pressWash(theme) };\n  }\n  return style;\n};\n\nexport interface RatingStarSpec {\n  readonly size: ControlSize;\n  readonly disabled?: boolean;\n  readonly readOnly?: boolean;\n}\n\nexport const ratingStarStyle = (\n  spec: RatingStarSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const interactive = spec.disabled !== true && spec.readOnly !== true;\n  const style: StyleDesc = {\n    position: \"relative\",\n    display: \"flex\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    padding: spaceValue(theme, \"2xs\"),\n    minWidth: targetSize.minimum,\n    minHeight: targetSize.minimum,\n    borderRadius: radiusValue(theme, \"full\"),\n    cursor: interactive ? \"pointer\" : \"not-allowed\",\n  };\n  if (spec.disabled === true) style.opacity = disabledOpacity;\n  return style;\n};\n\nexport const ratingPartialStyle = (fill: number): StyleDesc => ({\n  position: \"absolute\",\n  top: 0,\n  bottom: 0,\n  left: 0,\n  width: `${Math.min(1, Math.max(0, fill)) * 100}%`,\n  overflow: \"hidden\",\n  pointerEvents: \"none\",\n});\n\nexport const ratingPartialCellStyle = (theme: PrimitiveTheme): StyleDesc => ({\n  position: \"absolute\",\n  top: 0,\n  bottom: 0,\n  left: 0,\n  display: \"flex\",\n  alignItems: \"center\",\n  justifyContent: \"center\",\n  padding: spaceValue(theme, \"2xs\"),\n  minWidth: targetSize.minimum,\n});\n\nexport const ratingStarColor = (\n  filled: boolean,\n  theme: PrimitiveTheme,\n  invalid?: boolean,\n): string | undefined =>\n  filled\n    ? invalid === true\n      ? colorRole(theme, \"destructive\")\n      : colorRole(theme, \"primary\")\n    : colorRole(theme, \"mutedForeground\");\n\nexport const otpGroupStyle = (theme: PrimitiveTheme): StyleDesc => ({\n  display: \"flex\",\n  flexDirection: \"row\",\n  alignItems: \"center\",\n  gap: spaceValue(theme, \"sm\"),\n});\n\nexport interface OtpCellSpec {\n  readonly size: ControlSize;\n  readonly invalid?: boolean;\n  readonly disabled?: boolean;\n}\n\nexport const otpCellStyle = (\n  spec: OtpCellSpec,\n  theme: PrimitiveTheme,\n): StyleDesc => {\n  const control = controlStyle(\n    {\n      variant: fieldVariant,\n      size: spec.size,\n      square: true,\n      invalid: spec.invalid,\n      disabled: spec.disabled,\n    },\n    theme,\n  );\n  const body = typeOf(theme, \"body\");\n  return {\n    ...control,\n    justifyContent: \"center\",\n    textAlign: \"center\",\n    fontSize: body.fontSize,\n    lineHeight: body.lineHeight,\n    fontWeight: body.fontWeight,\n    cursor: spec.disabled === true ? \"not-allowed\" : \"text\",\n  };\n};\n"
    },
    {
      "path": "packages/primitives/src/core/theme.ts",
      "target": "@ui/gpuixui/primitives/core/theme.ts",
      "type": "registry:lib",
      "content": "import {\n  borderWidth as libraryBorderWidth,\n  defaultDensity as libraryDensity,\n  iconSize as libraryIconSize,\n  isControlSize,\n  isElevationLevel,\n  isTextRole,\n  radius as libraryRadius,\n  spacing as librarySpacing,\n  themeColors,\n  typography as libraryTypography,\n  type ControlSize,\n  type ElevationLevel,\n  type ThemeColors,\n  type ThemeContrast,\n} from \"../../tokens/index.js\";\nimport type {\n  BorderWidth,\n  Density,\n  IconSize,\n  Radius,\n  Spacing,\n  TextRole,\n  TextStyle,\n} from \"../../tokens/index.js\";\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport * as R from \"effect/Record\";\n\nexport { isTextRole };\n\nexport type {\n  BoxShadow as ThemeShadow,\n  BorderWidth,\n  ControlSize,\n  Density,\n  DensityName,\n  Elevation,\n  ElevationLevel,\n  FontWeight,\n  IconSize as ThemeIconSize,\n  Radius,\n  Spacing,\n  TextStyle,\n  TextRole,\n  Theme as PrimitiveTheme,\n  ThemeColors,\n  ThemeMode,\n  Typography,\n} from \"../../tokens/index.js\";\n\nexport const controlSize = (size: unknown): ControlSize =>\n  isControlSize(size) ? size : \"md\";\n\nexport const elevationLevel = (level: unknown): ElevationLevel | undefined =>\n  Option.match(Option.fromNullishOr(level), {\n    onNone: () => undefined,\n    onSome: (value) => (isElevationLevel(value) ? value : \"md\"),\n  });\n\nexport type SpaceStep = keyof Spacing;\n\nexport type RadiusStep = keyof Radius;\n\nexport interface SpaceSource {\n  readonly spacing?: Spacing | null;\n}\n\nexport const isSpaceStep = (\n  theme: SpaceSource,\n  step: unknown,\n): step is SpaceStep => {\n  const scale = theme.spacing;\n  if (scale === undefined || scale === null || !P.isString(step)) return false;\n  return Arr.some(R.keys(scale), (name) => name === step);\n};\n\nexport const spaceStep = (\n  theme: SpaceSource,\n  step: unknown,\n): SpaceStep | undefined => (isSpaceStep(theme, step) ? step : undefined);\n\nexport const scaleValue = <Step extends string>(\n  scale: Readonly<Partial<Record<Step, number>>> | null | undefined,\n  library: Readonly<Record<Step, number>>,\n  step: Step,\n): number => {\n  const value = scale === null || scale === undefined ? undefined : scale[step];\n  return P.isNumber(value) && Number.isFinite(value) ? value : library[step];\n};\n\nexport const spaceValue = (theme: SpaceSource, step: SpaceStep): number =>\n  scaleValue(theme.spacing, librarySpacing, step);\n\nexport interface RadiusSource {\n  readonly radius?: Radius | null;\n}\n\nexport const radiusValue = (theme: RadiusSource, step: RadiusStep): number =>\n  scaleValue(theme.radius, libraryRadius, step);\n\nconst RADIUS_STEPS: ReadonlyArray<string> = R.keys(libraryRadius);\n\nexport const isRadiusStep = (step: unknown): step is RadiusStep =>\n  P.isString(step) && Arr.contains(RADIUS_STEPS, step);\n\nexport const radiusStep = (step: unknown): RadiusStep | undefined =>\n  isRadiusStep(step) ? step : undefined;\n\nexport interface BorderWidthSource {\n  readonly borderWidth?: Partial<BorderWidth> | null;\n}\n\nexport const borderWidthOf = (\n  theme: BorderWidthSource,\n  step: keyof BorderWidth,\n): number => scaleValue(theme.borderWidth, libraryBorderWidth, step);\n\nexport interface DensitySource {\n  readonly density?: Density | null;\n}\n\nexport const controlHeightOf = (\n  theme: DensitySource,\n  size: ControlSize,\n): number =>\n  scaleValue(\n    theme.density?.controlHeight,\n    libraryDensity.controlHeight,\n    controlSize(size),\n  );\n\nexport const controlPaddingOf = (\n  theme: DensitySource,\n  size: ControlSize,\n): number =>\n  scaleValue(\n    theme.density?.controlPaddingX,\n    libraryDensity.controlPaddingX,\n    controlSize(size),\n  );\n\nexport const rowHeightOf = (theme: DensitySource): number => {\n  const height = theme.density?.rowHeight;\n  return P.isNumber(height) && Number.isFinite(height)\n    ? height\n    : libraryDensity.rowHeight;\n};\n\nexport interface IconSizeSource {\n  readonly iconSize?: IconSize | null;\n}\n\nexport const iconSideOf = (theme: IconSizeSource, size: ControlSize): number =>\n  scaleValue(theme.iconSize, libraryIconSize, controlSize(size));\n\nconst ICON_STEPS: ReadonlyArray<string> = R.keys(libraryIconSize);\n\nexport const isIconStep = (step: unknown): step is keyof IconSize =>\n  P.isString(step) && Arr.contains(ICON_STEPS, step);\n\nexport const iconStepValue = (theme: IconSizeSource, step: unknown): number =>\n  scaleValue(theme.iconSize, libraryIconSize, isIconStep(step) ? step : \"md\");\n\nexport interface TypeSource {\n  readonly typography?: {\n    readonly sans?: string | null;\n    readonly mono?: string | null;\n  } & Partial<Record<TextRole, TextStyle | null>>;\n}\n\nconst isTextStyle = (value: unknown): value is TextStyle =>\n  typeof value === \"object\" &&\n  value !== null &&\n  \"fontSize\" in value &&\n  \"lineHeight\" in value &&\n  \"fontWeight\" in value &&\n  P.isNumber(value.fontSize) &&\n  P.isNumber(value.lineHeight) &&\n  P.isNumber(value.fontWeight);\n\nexport const typeOf = (theme: TypeSource, role: unknown): TextStyle => {\n  const name = isTextRole(role) ? role : \"body\";\n  const style: unknown = theme.typography?.[name];\n  return isTextStyle(style) ? style : libraryTypography[name];\n};\n\nexport const familyOf = (\n  theme: TypeSource,\n  family: \"sans\" | \"mono\",\n): string => {\n  const name: unknown = theme.typography?.[family];\n  return P.isString(name) && name !== \"\" ? name : libraryTypography[family];\n};\n\nexport interface ColorSource {\n  readonly colors?: Partial<ThemeColors>;\n}\n\nexport type PaintSource = ColorSource & {\n  readonly contrast?: ThemeContrast;\n};\n\nexport const colorsOf = (theme: ColorSource): Partial<ThemeColors> =>\n  themeColors(theme);\n\nexport const colorRole = (\n  theme: ColorSource,\n  role: keyof ThemeColors,\n): string | undefined => colorsOf(theme)[role];\n\nexport type ColorRef = (theme: PaintSource) => string | undefined;\n"
    },
    {
      "path": "packages/primitives/src/core/value.ts",
      "target": "@ui/gpuixui/primitives/core/value.ts",
      "type": "registry:lib",
      "content": "import * as Option from \"effect/Option\";\nimport { storedNumber } from \"./stored.js\";\n\nexport interface ValueRange {\n  readonly min: number;\n  readonly max: number;\n  readonly step: number;\n}\n\nexport interface TrackBox {\n  readonly x: number;\n  readonly width: number;\n}\n\nexport interface VerticalTrackBox {\n  readonly y: number;\n  readonly height: number;\n}\n\nexport interface ValueSpan {\n  readonly start: number;\n  readonly end: number;\n}\n\nconst finiteOr = (value: number, fallback: number): number =>\n  Number.isFinite(value) ? value : fallback;\n\nexport const normalizeRange = (range: ValueRange): ValueRange => {\n  const min = finiteOr(range.min, 0);\n  const max = finiteOr(range.max, min + 1);\n  const step = finiteOr(range.step, 0) > 0 ? Math.abs(range.step) : 0;\n  return min <= max ? { min, max, step } : { min: max, max: min, step };\n};\n\nexport interface StoredRange {\n  readonly min?: unknown;\n  readonly max?: unknown;\n  readonly step?: unknown;\n}\n\nexport const storedRange = (\n  bounds: StoredRange,\n  defaults: ValueRange,\n): ValueRange =>\n  normalizeRange({\n    min: Option.getOrElse(storedNumber(bounds.min), () => defaults.min),\n    max: Option.getOrElse(storedNumber(bounds.max), () => defaults.max),\n    step: Option.getOrElse(storedNumber(bounds.step), () => defaults.step),\n  });\n\nconst cleanStep = (value: number): number =>\n  Number.parseFloat(value.toPrecision(15));\n\nexport const clampValue = (value: number, range: ValueRange): number => {\n  const { min, max, step } = normalizeRange(range);\n  if (!Number.isFinite(value)) return min;\n  const clamped = Math.min(max, Math.max(min, value));\n  if (step === 0) return clamped;\n  if (clamped >= max) return max;\n  const snapped = min + Math.round((clamped - min) / step) * step;\n  return Math.min(max, Math.max(min, cleanStep(snapped)));\n};\n\nexport const valueFraction = (value: number, range: ValueRange): number => {\n  const { min, max } = normalizeRange(range);\n  const span = max - min;\n  if (span <= 0) return 0;\n  return (clampValue(value, range) - min) / span;\n};\n\nexport const fractionValue = (fraction: number, range: ValueRange): number => {\n  const { min, max } = normalizeRange(range);\n  const bounded = Number.isFinite(fraction)\n    ? Math.min(1, Math.max(0, fraction))\n    : 0;\n  return clampValue(min + bounded * (max - min), range);\n};\n\nexport const valueStep = (range: ValueRange): number => {\n  const { min, max, step } = normalizeRange(range);\n  if (step > 0) return step;\n  const span = max - min;\n  return span > 0 ? span / 100 : 1;\n};\n\nexport const stepValue = (\n  value: number,\n  range: ValueRange,\n  steps: number,\n): number => {\n  const normalized = normalizeRange(range);\n  const base = clampValue(value, normalized);\n  if (steps === 0) return base;\n  if (normalized.step === 0)\n    return clampValue(base + steps * valueStep(normalized), normalized);\n  const offset = (base - normalized.min) / normalized.step;\n  const nearest = Math.round(offset);\n\n  const index =\n    Math.abs(offset - nearest) < 1e-9\n      ? nearest\n      : steps > 0\n        ? Math.floor(offset)\n        : Math.ceil(offset);\n  return clampValue(\n    normalized.min + (index + steps) * normalized.step,\n    normalized,\n  );\n};\n\nconst ARROW_STEPS: Readonly<Record<string, number>> = {\n  left: -1,\n  down: -1,\n  right: 1,\n  up: 1,\n};\n\nexport const decideValueKey = (\n  value: number,\n  range: ValueRange,\n  key: string,\n  pageSteps = 10,\n): Option.Option<number> => {\n  const normalized = normalizeRange(range);\n  if (key === \"home\") return Option.some(normalized.min);\n  if (key === \"end\") return Option.some(normalized.max);\n  const steps =\n    key === \"pageup\"\n      ? pageSteps\n      : key === \"pagedown\"\n        ? -pageSteps\n        : ARROW_STEPS[key];\n  return steps === undefined\n    ? Option.none()\n    : Option.some(stepValue(value, normalized, steps));\n};\n\nexport const pointerValue = (\n  x: number,\n  box: TrackBox,\n  range: ValueRange,\n  inset = 0,\n): number => {\n  const span = box.width - 2 * inset;\n  if (!Number.isFinite(x) || span <= 0) return normalizeRange(range).min;\n  return fractionValue((x - box.x - inset) / span, range);\n};\n\nexport const verticalPointerValue = (\n  y: number,\n  box: VerticalTrackBox,\n  range: ValueRange,\n  inset = 0,\n): number => {\n  const span = box.height - 2 * inset;\n  if (!Number.isFinite(y) || span <= 0) return normalizeRange(range).min;\n  return fractionValue(1 - (y - box.y - inset) / span, range);\n};\n\nexport const normalizeSpan = (\n  span: ValueSpan,\n  range: ValueRange,\n): ValueSpan => {\n  const start = clampValue(span.start, range);\n  const end = clampValue(span.end, range);\n  return start <= end ? { start, end } : { start: end, end: start };\n};\n\nexport const nearestEnd = (value: number, span: ValueSpan): \"start\" | \"end\" => {\n  if (value <= span.start) return \"start\";\n  if (value >= span.end) return \"end\";\n  return value - span.start <= span.end - value ? \"start\" : \"end\";\n};\n\nexport const spanFrom = (\n  point: number,\n  value: number,\n  range: ValueRange,\n): ValueSpan => {\n  const pinned = clampValue(point, range);\n  const moved = clampValue(value, range);\n  return moved >= pinned\n    ? { start: pinned, end: moved }\n    : { start: moved, end: pinned };\n};\n\nexport const moveSpanEnd = (\n  span: ValueSpan,\n  end: \"start\" | \"end\",\n  value: number,\n  range: ValueRange,\n): ValueSpan => {\n  const current = normalizeSpan(span, range);\n  return end === \"start\"\n    ? {\n        start: Math.min(clampValue(value, range), current.end),\n        end: current.end,\n      }\n    : {\n        start: current.start,\n        end: Math.max(clampValue(value, range), current.start),\n      };\n};\n"
    },
    {
      "path": "packages/primitives/src/core/variants.ts",
      "target": "@ui/gpuixui/primitives/core/variants.ts",
      "type": "registry:lib",
      "content": "import * as Arr from \"effect/Array\";\nimport type { StyleDesc } from \"@gpuix/react\";\nimport {\n  brandText,\n  filledHover,\n  filledPressed,\n  type FilledAction,\n} from \"../../tokens/index.js\";\nimport type { SpinnerAccent } from \"./loading.js\";\nimport { pressWash } from \"./pointer.js\";\nimport { colorRole, type PaintSource, type PrimitiveTheme } from \"./theme.js\";\nimport type { ControlVariant } from \"./style.js\";\n\nexport const BUTTON_VARIANTS = [\n  \"primary\",\n  \"secondary\",\n  \"outline\",\n  \"ghost\",\n  \"destructive\",\n  \"link\",\n] as const;\n\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\nconst VARIANT_NAMES: ReadonlyArray<string> = BUTTON_VARIANTS;\n\nexport const isButtonVariant = (value: unknown): value is ButtonVariant =>\n  typeof value === \"string\" && Arr.contains(VARIANT_NAMES, value);\n\nexport const buttonVariantOf = (value: unknown): ButtonVariant =>\n  isButtonVariant(value) ? value : \"primary\";\n\nconst filled = (\n  action: FilledAction,\n): Pick<ControlVariant, \"hover\" | \"active\" | \"filledAction\"> => ({\n  filledAction: action,\n  hover: (theme) => ({ backgroundColor: filledHover(theme, action) }),\n  active: (theme) => ({ backgroundColor: filledPressed(theme, action) }),\n});\n\nconst primary: ControlVariant = {\n  background: (theme) => colorRole(theme, \"primary\"),\n  foreground: (theme) => colorRole(theme, \"primaryForeground\"),\n  border: undefined,\n  ...filled(\"primary\"),\n};\n\nconst secondary: ControlVariant = {\n  background: (theme) => colorRole(theme, \"secondary\"),\n  foreground: (theme) => colorRole(theme, \"secondaryForeground\"),\n  border: (theme) => colorRole(theme, \"borderStrong\"),\n  ...filled(\"secondary\"),\n};\n\nconst washed: Pick<ControlVariant, \"hover\" | \"active\"> = {\n  hover: (theme) => ({ backgroundColor: colorRole(theme, \"hover\") }),\n  active: (theme) => ({ backgroundColor: pressWash(theme) }),\n};\n\nconst outline: ControlVariant = {\n  background: undefined,\n  foreground: (theme) => colorRole(theme, \"foreground\"),\n  border: (theme) => colorRole(theme, \"borderStrong\"),\n  ...washed,\n};\n\nconst ghost: ControlVariant = {\n  background: undefined,\n  foreground: (theme) => colorRole(theme, \"foreground\"),\n  border: undefined,\n  ...washed,\n};\n\nconst destructive: ControlVariant = {\n  background: (theme) => colorRole(theme, \"destructive\"),\n  foreground: (theme) => colorRole(theme, \"destructiveForeground\"),\n  border: undefined,\n  ...filled(\"destructive\"),\n};\n\nconst link: ControlVariant = {\n  background: undefined,\n\n  foreground: brandText,\n  border: undefined,\n  hover: (theme) => ({\n    backgroundColor: colorRole(theme, \"hover\"),\n    textDecoration: \"underline\",\n  }),\n  active: (theme) => ({ backgroundColor: pressWash(theme) }),\n};\n\nexport const buttonVariants: Record<ButtonVariant, ControlVariant> = {\n  primary,\n  secondary,\n  outline,\n  ghost,\n  destructive,\n  link,\n};\n\nexport function actionOnColor(\n  theme: PrimitiveTheme,\n  variant: ButtonVariant,\n): string;\nexport function actionOnColor(\n  theme: PaintSource,\n  variant: ButtonVariant,\n): string | undefined;\nexport function actionOnColor(\n  theme: PaintSource,\n  variant: ButtonVariant,\n): string | undefined {\n  return buttonVariants[buttonVariantOf(variant)].foreground(theme);\n}\n\nconst FILLED_SPINNER_ACCENT: Readonly<\n  Partial<Record<ButtonVariant, SpinnerAccent>>\n> = {\n  primary: \"on-primary\",\n  secondary: \"on-secondary\",\n  destructive: \"on-destructive\",\n};\n\nexport const variantSpinnerAccent = (\n  variant: ButtonVariant,\n  disabled = false,\n): SpinnerAccent => {\n  const name = buttonVariantOf(variant);\n  return disabled && paintsFilledSurface(name)\n    ? \"on-disabled\"\n    : (FILLED_SPINNER_ACCENT[name] ?? \"primary\");\n};\n\nexport const toggleSpinnerAccent = (\n  pressed: boolean,\n  disabled = false,\n): SpinnerAccent => {\n  if (pressed) return disabled ? \"on-disabled\" : \"on-accent\";\n  return \"primary\";\n};\n\nexport const paintsFilledSurface = (variant: ButtonVariant): boolean =>\n  buttonVariants[buttonVariantOf(variant)].filledAction !== undefined;\n\nexport const fieldVariant: ControlVariant = {\n  background: (theme) => colorRole(theme, \"input\"),\n  foreground: (theme) => colorRole(theme, \"inputForeground\"),\n  border: (theme) => colorRole(theme, \"border\"),\n  hover: (theme) => ({ borderColor: colorRole(theme, \"borderStrong\") }),\n  active: (theme) => ({ borderColor: colorRole(theme, \"ring\") }),\n};\n\nexport const variantTable: ReadonlyArray<{\n  readonly name: ButtonVariant;\n  readonly variant: ControlVariant;\n  readonly description: string;\n}> = [\n  { name: \"primary\", variant: primary, description: \"The brand action.\" },\n  { name: \"secondary\", variant: secondary, description: \"Filled, neutral.\" },\n  { name: \"outline\", variant: outline, description: \"Border only.\" },\n  { name: \"ghost\", variant: ghost, description: \"No chrome at rest.\" },\n  {\n    name: \"destructive\",\n    variant: destructive,\n    description: \"Deletes, revokes, disconnects.\",\n  },\n  { name: \"link\", variant: link, description: \"A text action.\" },\n];\n\nexport const TOGGLE_VARIANTS = [\"default\", \"outline\"] as const;\n\nexport type ToggleVariant = (typeof TOGGLE_VARIANTS)[number];\n\nconst TOGGLE_NAMES: ReadonlyArray<string> = TOGGLE_VARIANTS;\n\nexport const isToggleVariant = (value: unknown): value is ToggleVariant =>\n  typeof value === \"string\" && Arr.contains(TOGGLE_NAMES, value);\n\nexport const toggleVariantOf = (value: unknown): ToggleVariant =>\n  isToggleVariant(value) ? value : \"default\";\n\nconst toggleRest: ControlVariant = {\n  background: undefined,\n  foreground: (theme) => colorRole(theme, \"foreground\"),\n  border: undefined,\n  ...washed,\n};\n\nconst toggleOutlineRest: ControlVariant = {\n  background: undefined,\n  foreground: (theme) => colorRole(theme, \"foreground\"),\n  border: (theme) => colorRole(theme, \"borderStrong\"),\n  ...washed,\n};\n\nconst togglePressedBase = (\n  border: ControlVariant[\"border\"],\n): ControlVariant => ({\n  background: (theme) => colorRole(theme, \"accent\"),\n  foreground: (theme) => colorRole(theme, \"accentForeground\"),\n  border,\n\n  hover: (theme) => ({ backgroundColor: filledHover(theme, \"accent\") }),\n  active: (theme) => ({ backgroundColor: filledPressed(theme, \"accent\") }),\n});\n\nexport const toggleVariants: Readonly<\n  Record<\n    ToggleVariant,\n    { readonly rest: ControlVariant; readonly pressed: ControlVariant }\n  >\n> = {\n  default: {\n    rest: toggleRest,\n    pressed: togglePressedBase(undefined),\n  },\n  outline: {\n    rest: toggleOutlineRest,\n    pressed: togglePressedBase((theme) => colorRole(theme, \"borderStrong\")),\n  },\n};\n\nexport const variantKey = (\n  variant: ControlVariant,\n  theme: PrimitiveTheme,\n): string =>\n  [\n    variant.background === undefined ? \"none\" : variant.background(theme),\n    variant.foreground(theme),\n    variant.border === undefined ? \"none\" : variant.border(theme),\n  ].join(\"/\");\n\nexport const hasPointerFeedback = (\n  variant: ControlVariant,\n  theme: PrimitiveTheme,\n): boolean => {\n  const feedback = (style: StyleDesc): boolean =>\n    style.backgroundColor !== undefined || style.borderColor !== undefined;\n  return feedback(variant.hover(theme)) && feedback(variant.active(theme));\n};\n"
    },
    {
      "path": "packages/primitives/src/index.ts",
      "target": "@ui/gpuixui/primitives/index.ts",
      "type": "registry:lib",
      "content": "export * from \"./core/index.js\";\nexport * from \"./components/index.js\";\n"
    },
    {
      "path": "packages/primitives/src/progress.ts",
      "target": "@ui/gpuixui/primitives/progress.ts",
      "type": "registry:lib",
      "content": "export {\n  Progress,\n  ProgressIndicator,\n  ProgressLabel,\n  ProgressTrack,\n  ProgressValue,\n} from \"./components/Progress.js\";\nexport type {\n  ProgressIndicatorProps,\n  ProgressLabelProps,\n  ProgressProps,\n  ProgressTrackProps,\n  ProgressValueProps,\n} from \"./components/Progress.js\";\nexport type { ProgressSize, ProgressTone } from \"./core/style.js\";\n"
    },
    {
      "path": "packages/primitives/src/recipe/index.ts",
      "target": "@ui/gpuixui/primitives/recipe/index.ts",
      "type": "registry:lib",
      "content": "import type { ControlSize, TextRole } from \"../../tokens/index.js\";\nimport { cn } from \"../../tw/index.js\";\nimport { elevationLevel, isTextRole, radiusStep } from \"../core/theme.js\";\nimport { stackDirection, trackCount } from \"../core/layout.js\";\nimport type {\n  BoxSpec,\n  GridSpec,\n  StackAlign,\n  StackJustify,\n  StackSpec,\n} from \"../core/layout.js\";\nimport type { SelectionAccent, SelectionState } from \"../core/selection.js\";\nimport {\n  lineClampOf,\n  paintableAspectRatio,\n  paddingSides,\n  progressSizeOf,\n  progressToneOf,\n  scrollAxis,\n  textAlignOf,\n  textToneOf,\n} from \"../core/style.js\";\nimport type {\n  ProgressSize,\n  ProgressTone,\n  SliderOrientation,\n  SurfaceTone,\n  TextAlign,\n  TextTone,\n} from \"../core/style.js\";\nimport type { ElevationLevel, RadiusStep, SpaceStep } from \"../core/theme.js\";\nimport { type Orientation, orientationOf } from \"../core/keyboard.js\";\nimport { selectionAccentOf } from \"../core/selection.js\";\nimport {\n  buttonVariantOf,\n  paintsFilledSurface,\n  toggleVariantOf,\n} from \"../core/variants.js\";\nimport type { ButtonVariant, ToggleVariant } from \"../core/variants.js\";\n\nconst BUTTON_BASE =\n  \"flex flex-row items-center justify-center gap-sm rounded-md type-label cursor-pointer disabled:cursor-not-allowed\";\n\nconst FILLED_DISABLED =\n  \"disabled:bg-disabled disabled:text-disabled-foreground\";\nconst UNFILLED_DISABLED = \"disabled:opacity-50\";\n\nconst BUTTON_VARIANT: Readonly<Record<ButtonVariant, string>> = {\n  primary:\n    \"bg-primary text-primary-foreground border-0 hover:fill-primary active:fill-primary-pressed\",\n  secondary:\n    \"bg-secondary text-secondary-foreground border border-strong hover:fill-secondary active:fill-secondary-pressed\",\n  outline:\n    \"text-foreground border border-strong hover:bg-hover active:bg-active\",\n  ghost: \"text-foreground border-0 hover:bg-hover active:bg-active\",\n  destructive:\n    \"bg-destructive text-destructive-foreground border-0 hover:fill-destructive active:fill-destructive-pressed\",\n  link: \"text-brand border-0 hover:bg-hover hover:underline active:bg-active\",\n};\n\nconst BUTTON_SIZE: Readonly<Record<ControlSize, string>> = {\n  sm: \"h-sm px-sm\",\n  md: \"h-md px-md\",\n  lg: \"h-lg px-lg\",\n};\n\nconst BUTTON_SQUARE: Readonly<Record<ControlSize, string>> = {\n  sm: \"size-sm px-0\",\n  md: \"size-md px-0\",\n  lg: \"size-lg px-0\",\n};\n\nexport interface ButtonRecipeOptions {\n  readonly square?: boolean;\n  readonly fullWidth?: boolean;\n}\n\nexport const buttonClass = (\n  variant: ButtonVariant,\n  size: ControlSize,\n  options: ButtonRecipeOptions = {},\n): string => {\n  const name = buttonVariantOf(variant);\n  return cn(\n    BUTTON_BASE,\n    BUTTON_VARIANT[name],\n    paintsFilledSurface(name) ? FILLED_DISABLED : UNFILLED_DISABLED,\n    options.square === true ? BUTTON_SQUARE[size] : BUTTON_SIZE[size],\n    options.fullWidth === true && \"w-full\",\n  );\n};\n\nconst FIELD_BASE =\n  \"flex flex-row items-center justify-start gap-sm rounded-md type-body cursor-text select-text bg-input text-input-foreground border border-border hover:border-border-strong active:border-ring disabled:opacity-50 disabled:cursor-not-allowed\";\n\nconst FIELD_INVALID = \"aria-invalid:border-destructive\";\n\nexport interface FieldRecipeOptions {\n  readonly size: ControlSize;\n  readonly fullWidth?: boolean;\n  readonly invalid?: boolean;\n}\n\nexport const fieldClass = (options: FieldRecipeOptions): string =>\n  cn(\n    FIELD_BASE,\n    BUTTON_SIZE[options.size],\n    options.invalid === true && FIELD_INVALID,\n    options.fullWidth === true && \"w-full\",\n  );\n\nconst ROLE_CLASS: Readonly<Record<TextRole, string>> = {\n  display: \"display\",\n  h1: \"h1\",\n  h2: \"h2\",\n  h3: \"h3\",\n  body: \"body\",\n  bodyStrong: \"body-strong\",\n  label: \"label\",\n  caption: \"caption\",\n  code: \"code\",\n  codeBlock: \"code-block\",\n};\n\nconst TONE_CLASS: Readonly<Record<TextTone, string | undefined>> = {\n  default: \"text-foreground\",\n  muted: \"text-muted-foreground\",\n\n  primary: \"text-brand\",\n\n  destructive: \"text-destructiveSoftForeground\",\n  inherit: undefined,\n};\n\nconst ALIGN_CLASS: Readonly<Record<TextAlign, string>> = {\n  start: \"text-start\",\n  center: \"text-center\",\n  end: \"text-end\",\n};\n\nconst MONO_ROLES: ReadonlyArray<TextRole> = [\"code\", \"codeBlock\"];\n\nconst familyOf = (role: TextRole): string =>\n  MONO_ROLES.includes(role) ? \"font-mono\" : \"font-sans\";\n\nexport interface TextRecipeOptions {\n  readonly role: TextRole;\n  readonly tone: TextTone;\n  readonly align?: TextAlign;\n  readonly truncate?: boolean;\n  readonly clamp?: number;\n  readonly selectable?: boolean;\n}\n\nexport const textClass = (options: TextRecipeOptions): string => {\n  const role = isTextRole(options.role) ? options.role : \"body\";\n  const align = textAlignOf(options.align);\n  const clamp = lineClampOf(options.clamp);\n  return cn(\n    `type-${ROLE_CLASS[role]}`,\n    familyOf(role),\n    TONE_CLASS[textToneOf(options.tone)],\n    align === undefined ? undefined : ALIGN_CLASS[align],\n    options.selectable === true ? \"select-text\" : \"select-none\",\n    options.truncate === true && \"whitespace-nowrap text-ellipsis\",\n\n    clamp === undefined ? undefined : `clamp-${ROLE_CLASS[role]}-${clamp}`,\n  );\n};\n\nconst ALIGN_TO_CLASS: Readonly<Record<StackAlign, string>> = {\n  start: \"items-start\",\n  center: \"items-center\",\n  end: \"items-end\",\n  stretch: \"items-stretch\",\n  baseline: \"items-baseline\",\n};\n\nconst JUSTIFY_TO_CLASS: Readonly<Record<StackJustify, string>> = {\n  start: \"justify-start\",\n  center: \"justify-center\",\n  end: \"justify-end\",\n  between: \"justify-between\",\n  around: \"justify-around\",\n};\n\nconst directionClass = (direction: unknown): string =>\n  stackDirection(direction) === \"row\" ? \"flex-row\" : \"flex-col\";\n\nexport const stackClass = (spec: StackSpec): string =>\n  cn(\n    \"flex\",\n    directionClass(spec.direction),\n    spec.gap === undefined ? undefined : `gap-${spec.gap}`,\n    spec.align === undefined ? undefined : ALIGN_TO_CLASS[spec.align],\n    spec.justify === undefined ? undefined : JUSTIFY_TO_CLASS[spec.justify],\n    spec.wrap === true && \"flex-wrap\",\n    spec.grow === true && \"flex-grow\",\n    spec.fullWidth === true && \"w-full\",\n    spec.fullHeight === true && \"h-full\",\n  );\n\nexport const gridClass = (spec: GridSpec): string =>\n  cn(\n    \"grid\",\n    `grid-cols-${trackCount(spec.columns)}`,\n    spec.rows === undefined ? undefined : `grid-rows-${trackCount(spec.rows)}`,\n    spec.gap === undefined ? undefined : `gap-${spec.gap}`,\n    spec.grow === true && \"flex-grow\",\n  );\n\nconst SURFACE_BG: Readonly<Record<SurfaceTone, string | undefined>> = {\n  none: undefined,\n  background: \"bg-background\",\n  card: \"bg-card\",\n  popover: \"bg-popover\",\n  muted: \"bg-muted\",\n  accent: \"bg-accent\",\n  selected: \"bg-selected\",\n  skeleton: \"bg-skeleton\",\n};\n\nconst ELEVATION_SHADOW: Readonly<Record<ElevationLevel, string | undefined>> = {\n  sm: \"shadow-sm\",\n  md: \"shadow-md\",\n  lg: \"shadow-lg\",\n  overlay: undefined,\n};\n\nconst elevationClass = (level: unknown): string | undefined => {\n  const resolved = elevationLevel(level);\n  return resolved === undefined ? undefined : ELEVATION_SHADOW[resolved];\n};\n\nconst boxPaddingClass = (spec: BoxSpec): string | undefined => {\n  const sides = paddingSides(spec);\n  if (spec.paddingX === undefined && spec.paddingY === undefined) {\n    return sides.top === undefined ? undefined : `p-${sides.top}`;\n  }\n  return cn(\n    sides.top === undefined ? undefined : `pt-${sides.top}`,\n    sides.right === undefined ? undefined : `pr-${sides.right}`,\n    sides.bottom === undefined ? undefined : `pb-${sides.bottom}`,\n    sides.left === undefined ? undefined : `pl-${sides.left}`,\n  );\n};\n\nexport const boxClass = (spec: BoxSpec): string => {\n  const corner = radiusStep(spec.radius);\n  return cn(\n    \"flex flex-col\",\n    SURFACE_BG[spec.tone],\n    boxPaddingClass(spec),\n    corner === undefined ? undefined : `rounded-${corner}`,\n    spec.border === true && \"border border-border\",\n    spec.border === true && spec.borderStyle === \"dashed\" && \"border-dashed\",\n    paintableAspectRatio(spec.aspectRatio) === undefined\n      ? undefined\n      : `aspect-ratio-${spec.aspectRatio}`,\n    elevationClass(spec.elevation),\n    spec.shrink === true ? \"shrink-1\" : \"shrink-0\",\n    spec.grow === true && \"flex-grow\",\n    spec.fullWidth === true && \"w-full\",\n    spec.fullHeight === true && \"h-full\",\n  );\n};\n\nconst TOGGLE_PAINT: Readonly<\n  Record<ToggleVariant, { readonly rest: string; readonly pressed: string }>\n> = {\n  default: {\n    rest: \"text-foreground border-0 hover:bg-hover active:bg-active\",\n    pressed:\n      \"bg-accent text-accent-foreground border-0 hover:fill-accent active:fill-accent-pressed\",\n  },\n  outline: {\n    rest: \"text-foreground border border-strong hover:bg-hover active:bg-active\",\n    pressed:\n      \"bg-accent text-accent-foreground border border-strong hover:fill-accent active:fill-accent-pressed\",\n  },\n};\n\nexport const toggleClass = (\n  variant: ToggleVariant,\n  pressed: boolean,\n  size: ControlSize,\n  options: ButtonRecipeOptions = {},\n): string =>\n  cn(\n    BUTTON_BASE,\n    TOGGLE_PAINT[toggleVariantOf(variant)][pressed ? \"pressed\" : \"rest\"],\n\n    pressed ? FILLED_DISABLED : UNFILLED_DISABLED,\n    options.square === true ? BUTTON_SQUARE[size] : BUTTON_SIZE[size],\n  );\n\nexport const separatorClass = (\n  orientation: Orientation,\n  inset?: SpaceStep,\n): string => {\n  const step = inset ?? \"0\";\n  return orientationOf(orientation, \"horizontal\") === \"horizontal\"\n    ? cn(\"h-px shrink-0 bg-border\", `ml-${step}`, `mr-${step}`)\n    : cn(\"w-px shrink-0 bg-border\", `mt-${step}`, `mb-${step}`);\n};\n\nexport const separatorGroupClass = \"flex flex-row items-center gap-sm\";\n\nexport const scrollClass = (axis: unknown): string => {\n  const resolved = scrollAxis(axis);\n  return resolved === \"horizontal\"\n    ? \"overflow-y-hidden overflow-x-scroll\"\n    : resolved === \"vertical\"\n      ? \"overflow-y-scroll overflow-x-hidden\"\n      : \"overflow-y-scroll overflow-x-scroll\";\n};\n\nexport const scrollFrameClass = (spec: {\n  readonly padding?: SpaceStep;\n  readonly radius?: RadiusStep;\n  readonly border?: boolean;\n}): string => {\n  const corner = radiusStep(spec.radius);\n  return cn(\n    \"flex flex-col overflow-hidden\",\n    spec.padding === undefined ? undefined : `p-${spec.padding}`,\n    corner === undefined ? undefined : `rounded-${corner}`,\n    spec.border === true && \"border border-border\",\n  );\n};\n\nexport const buttonGroupClass = (\n  mode: \"attached\" | \"separated\",\n  gap: SpaceStep,\n  orientation: \"horizontal\" | \"vertical\",\n): string =>\n  cn(\n    \"flex\",\n    orientation === \"vertical\"\n      ? \"flex-col items-stretch\"\n      : \"flex-row items-center\",\n    mode === \"attached\" ? \"gap-0 rounded-md overflow-hidden\" : `gap-${gap}`,\n  );\n\nconst FIELD_SQUARE_BASE =\n  \"flex flex-row items-center justify-center gap-sm rounded-md type-body text-center bg-input text-input-foreground border border-border cursor-text hover:border-border-strong active:border-ring disabled:opacity-50 disabled:cursor-not-allowed\";\n\nexport const fieldSquareClass = (options: {\n  readonly size: ControlSize;\n  readonly invalid?: boolean;\n}): string =>\n  cn(\n    FIELD_SQUARE_BASE,\n    `size-${options.size}`,\n    \"px-0\",\n    options.invalid === true && FIELD_INVALID,\n  );\n\nexport const otpGroupClass = \"flex flex-row items-center gap-sm\";\n\nexport const ratingStarClass = (options: {\n  readonly disabled?: boolean;\n  readonly readOnly?: boolean;\n}): string => {\n  const interactive = options.disabled !== true && options.readOnly !== true;\n  return cn(\n    \"relative flex items-center justify-center p-2xs rounded-full min-target\",\n    interactive ? \"cursor-pointer\" : \"cursor-not-allowed\",\n    options.disabled === true && \"disabled:opacity-50\",\n  );\n};\n\nexport const ratingGroupClass = \"flex flex-row items-center gap-2xs\";\n\nexport const tooltipClass =\n  \"flex flex-col bg-popover text-popover-foreground rounded-sm border border-border pl-sm pr-sm pt-xs pb-xs type-size-caption whitespace-nowrap\";\n\nconst SELECT_TRIGGER_BASE =\n  \"flex flex-row items-center justify-between gap-sm rounded-md type-label bg-input text-input-foreground border border-border cursor-pointer hover:border-border-strong active:border-ring disabled:opacity-50 disabled:cursor-not-allowed\";\n\nexport const selectTriggerClass = (options: {\n  readonly size: ControlSize;\n  readonly invalid?: boolean;\n  readonly fullWidth?: boolean;\n}): string =>\n  cn(\n    SELECT_TRIGGER_BASE,\n    BUTTON_SIZE[options.size],\n    options.invalid === true && FIELD_INVALID,\n    options.fullWidth === true && \"w-full\",\n  );\n\nexport const selectContentClass =\n  \"flex flex-col bg-popover rounded-md border border-border p-xs gap-2xs min-w-full shadow-lg\";\n\nexport const selectItemRowClass =\n  \"flex flex-row items-center justify-between w-full\";\n\nexport const selectLabelClass =\n  \"flex flex-row items-center pl-sm pr-sm py-2xs type-size-caption text-muted-foreground\";\n\nexport const selectSeparatorClass = \"h-px w-full bg-border\";\n\nexport interface SelectItemPaint {\n  readonly selected: boolean;\n  readonly highlighted: boolean;\n  readonly disabled: boolean;\n}\n\nexport const selectItemClass = (state: SelectItemPaint): string =>\n  cn(\n    \"flex flex-row items-center h-sm pl-sm pr-sm rounded-sm type-size-label\",\n    state.disabled === true\n      ? \"text-disabled-foreground cursor-not-allowed\"\n      : \"text-popover-foreground cursor-pointer\",\n    state.disabled === true\n      ? undefined\n      : state.selected === true\n        ? \"bg-selected\"\n        : state.highlighted === true\n          ? \"bg-hover\"\n          : undefined,\n  );\n\nexport const numberFieldClass = (options: FieldRecipeOptions): string =>\n  cn(fieldClass(options), \"pr-0\");\n\nexport const numberStepperColumnClass =\n  \"flex flex-col h-full shrink-0 w-stepper-column\";\n\nexport const numberStepperClass = (disabled: boolean): string =>\n  cn(\n    \"flex items-center justify-center flex-grow rounded-sm\",\n    disabled\n      ? \"cursor-not-allowed\"\n      : \"cursor-pointer hover:bg-hover active:bg-active\",\n  );\n\nexport const numberInputClass = \"flex-grow h-full min-w-0 px-0 border-0\";\n\nconst SELECTION_ROLE: Readonly<\n  Record<SelectionAccent, { readonly bg: string; readonly fg: string }>\n> = {\n  primary: { bg: \"primary\", fg: \"primary-foreground\" },\n  destructive: { bg: \"destructive\", fg: \"destructive-foreground\" },\n};\n\nexport interface SelectionRecipeInput {\n  readonly size: ControlSize;\n  readonly accent: SelectionAccent;\n  readonly state: SelectionState;\n  readonly shape?: \"square\" | \"circle\";\n  readonly invalid?: boolean;\n}\n\nexport const selectionBoxClass = ({\n  size,\n  accent,\n  state,\n  shape = \"square\",\n  invalid,\n}: SelectionRecipeInput): string => {\n  const filled = state !== \"unchecked\";\n  const { bg, fg } = SELECTION_ROLE[selectionAccentOf(accent)];\n  return cn(\n    \"flex items-center justify-center shrink-0 border cursor-pointer\",\n    `size-icon-${size}`,\n    shape === \"circle\" ? \"rounded-full\" : \"rounded-sm\",\n    \"disabled:opacity-50 disabled:cursor-not-allowed\",\n    filled\n      ? cn(\n          `bg-${bg} border-${bg} text-${fg}`,\n\n          `hover:fill-${bg} hover:border-transparent active:fill-${bg}-pressed active:border-transparent`,\n        )\n      : cn(\n          invalid === true ? \"border-destructive\" : \"border-strong\",\n          invalid === true ? \"text-destructive\" : \"text-muted-foreground\",\n          \"hover:bg-hover active:bg-active\",\n        ),\n  );\n};\n\nexport const radioDotClass = ({\n  size,\n  accent,\n  state,\n  invalid,\n}: SelectionRecipeInput): string => {\n  const filled = state !== \"unchecked\";\n  return cn(\n    \"shrink-0 rounded-full\",\n    `size-dot-${size}`,\n    filled\n      ? `bg-${SELECTION_ROLE[selectionAccentOf(accent)].fg}`\n      : invalid === true\n        ? \"bg-destructive\"\n        : \"bg-muted-foreground\",\n  );\n};\n\nexport const switchTrackClass = ({\n  size,\n  accent,\n  state,\n  invalid,\n}: SelectionRecipeInput): string => {\n  const filled = state !== \"unchecked\";\n  const { bg } = SELECTION_ROLE[selectionAccentOf(accent)];\n  return cn(\n    \"relative flex flex-row items-center shrink-0 rounded-full border cursor-pointer\",\n    `switch-track-${size}`,\n    \"disabled:opacity-50 disabled:cursor-not-allowed\",\n    filled\n      ? cn(\n          `bg-${bg} border-${bg}`,\n\n          `hover:fill-${bg} hover:border-transparent active:fill-${bg}-pressed active:border-transparent`,\n        )\n      : cn(\n          \"bg-muted\",\n          invalid === true ? \"border-destructive\" : \"border-strong\",\n          \"hover:bg-hover active:bg-active\",\n        ),\n  );\n};\n\nexport const switchThumbClass = ({\n  size,\n  accent,\n  state,\n}: SelectionRecipeInput): string => {\n  const filled = state !== \"unchecked\";\n  return cn(\n    \"absolute shrink-0 rounded-full pointer-events-none\",\n    `switch-thumb-${size}`,\n    `size-thumb-${size}`,\n    filled ? `switch-thumb-on-${size}` : undefined,\n    filled\n      ? `bg-${SELECTION_ROLE[selectionAccentOf(accent)].fg}`\n      : \"bg-foreground\",\n  );\n};\n\nexport const sliderTrackClass = (\n  size: ControlSize,\n  orientation: SliderOrientation = \"horizontal\",\n): string =>\n  orientation === \"vertical\"\n    ? cn(\n        \"flex flex-col items-center h-full cursor-pointer\",\n        `w-control-${size}`,\n        `py-inset-${size}`,\n        \"disabled:opacity-50 disabled:cursor-not-allowed\",\n      )\n    : cn(\n        \"flex flex-row items-center w-full cursor-pointer\",\n        `h-${size}`,\n        `px-inset-${size}`,\n        \"disabled:opacity-50 disabled:cursor-not-allowed\",\n      );\n\nexport const sliderRailClass = (\n  orientation: SliderOrientation = \"horizontal\",\n): string =>\n  orientation === \"vertical\"\n    ? \"h-full w-space-xs rounded-full bg-muted flex flex-col justify-end items-center pointer-events-none\"\n    : \"w-full h-space-xs rounded-full bg-muted flex flex-row items-center pointer-events-none\";\n\nexport const sliderFillClass = (\n  invalid: boolean,\n  orientation: SliderOrientation = \"horizontal\",\n): string =>\n  cn(\n    \"shrink-0 rounded-full pointer-events-none\",\n    orientation === \"vertical\" ? \"w-full\" : \"h-full\",\n    invalid ? \"bg-destructive\" : \"bg-primary\",\n  );\n\nexport const sliderThumbClass = (\n  size: ControlSize,\n  invalid: boolean,\n  orientation: SliderOrientation = \"horizontal\",\n): string =>\n  cn(\n    orientation === \"vertical\"\n      ? `slider-thumb-v-${size}`\n      : `slider-thumb-${size}`,\n    \"shrink-0 rounded-full bg-input pointer-events-none\",\n    invalid ? \"border-destructive\" : \"border-primary\",\n  );\n\nconst PROGRESS_HEIGHT_STEP: Readonly<Record<ProgressSize, SpaceStep>> = {\n  sm: \"xs\",\n  md: \"sm\",\n};\n\nexport const progressTrackClass = (size: ProgressSize): string =>\n  `flex flex-row w-full h-space-${PROGRESS_HEIGHT_STEP[progressSizeOf(size)]} rounded-full bg-muted overflow-hidden`;\n\nconst PROGRESS_TONE_CLASS: Readonly<Record<ProgressTone, string>> = {\n  primary: \"bg-primary\",\n  success: \"bg-success\",\n  destructive: \"bg-destructive\",\n  muted: \"bg-muted-foreground\",\n};\n\nexport const progressFillClass = (tone: ProgressTone): string =>\n  cn(\"h-full rounded-full\", PROGRESS_TONE_CLASS[progressToneOf(tone)]);\n\nexport const progressBufferClass = (tone: ProgressTone): string =>\n  cn(\n    \"h-full shrink-0\",\n    progressToneOf(tone) === \"muted\"\n      ? \"bg-border-strong\"\n      : \"bg-muted-foreground\",\n  );\n\nexport const spinnerGroupClass = \"relative shrink-0\";\n\nexport const skeletonClass = (spec: {\n  readonly circle?: boolean;\n  readonly radius?: RadiusStep;\n}): string =>\n  cn(\n    \"shrink-0 bg-skeleton\",\n\n    spec.circle === true\n      ? \"rounded-full\"\n      : `rounded-${radiusStep(spec.radius) ?? \"sm\"}`,\n  );\n"
    },
    {
      "path": "packages/primitives/src/recipe/registry.ts",
      "target": "@ui/gpuixui/primitives/recipe/registry.ts",
      "type": "registry:lib",
      "content": "import type { StyleDesc } from \"@gpuix/react\";\nimport type { ControlSize, FilledRole, TextRole, Theme } from \"../../tokens/index.js\";\nimport {\n  brandText,\n  filledHover,\n  filledPressed,\n  targetSize,\n} from \"../../tokens/index.js\";\nimport { createTw, type TwCompiler } from \"../../tw/index.js\";\nimport { pipe } from \"effect\";\nimport * as Option from \"effect/Option\";\nimport * as Str from \"effect/String\";\nimport { switchBox, switchThumbOffset } from \"../core/selection.js\";\nimport { pressWash } from \"../core/pointer.js\";\nimport { paintableAspectRatio, textAlignValue } from \"../core/style.js\";\nimport type { RadiusStep, SpaceStep } from \"../core/theme.js\";\nimport {\n  borderWidthOf,\n  colorRole,\n  controlHeightOf,\n  controlPaddingOf,\n  familyOf,\n  iconSideOf,\n  radiusValue,\n  typeOf,\n  spaceValue,\n} from \"../core/theme.js\";\n\nconst SPACING_STEP: Readonly<Partial<Record<string, SpaceStep>>> = {\n  \"2xs\": \"2xs\",\n  xs: \"xs\",\n  sm: \"sm\",\n  md: \"md\",\n  lg: \"lg\",\n  xl: \"xl\",\n  \"2xl\": \"2xl\",\n  \"3xl\": \"3xl\",\n  \"4xl\": \"4xl\",\n  \"5xl\": \"5xl\",\n};\n\nconst RADIUS_STEP: Readonly<Partial<Record<string, RadiusStep>>> = {\n  none: \"none\",\n  sm: \"sm\",\n  md: \"md\",\n  lg: \"lg\",\n  xl: \"xl\",\n  full: \"full\",\n};\n\nconst CONTROL_SIZE: Readonly<Partial<Record<string, ControlSize>>> = {\n  sm: \"sm\",\n  md: \"md\",\n  lg: \"lg\",\n};\n\nconst TEXT_ROLE: Readonly<Partial<Record<string, TextRole>>> = {\n  display: \"display\",\n  h1: \"h1\",\n  h2: \"h2\",\n  h3: \"h3\",\n  body: \"body\",\n  \"body-strong\": \"bodyStrong\",\n  label: \"label\",\n  caption: \"caption\",\n  code: \"code\",\n  \"code-block\": \"codeBlock\",\n};\n\nconst spacingStep = (value: string | undefined): SpaceStep | undefined =>\n  value === undefined ? undefined : SPACING_STEP[value];\n\nconst controlSize = (value: string | undefined): ControlSize | undefined =>\n  value === undefined ? undefined : CONTROL_SIZE[value];\n\nconst iconSide = (\n  theme: Theme,\n  value: string | undefined,\n): number | undefined => {\n  const size = controlSize(value);\n  return size === undefined ? undefined : iconSideOf(theme, size);\n};\n\nconst halfIconSide = (\n  theme: Theme,\n  value: string | undefined,\n): number | undefined => {\n  const side = iconSide(theme, value);\n  return side === undefined ? undefined : Math.round(side / 2);\n};\n\nconst roleType = (\n  theme: Theme,\n  role: TextRole,\n): Pick<StyleDesc, \"fontSize\" | \"lineHeight\" | \"fontWeight\"> => {\n  const type = typeOf(theme, role);\n  return {\n    fontSize: type.fontSize,\n    lineHeight: type.lineHeight,\n    fontWeight: type.fontWeight,\n  };\n};\n\nconst roleSize = (\n  theme: Theme,\n  role: TextRole,\n): Pick<StyleDesc, \"fontSize\" | \"lineHeight\"> => {\n  const type = typeOf(theme, role);\n  return { fontSize: type.fontSize, lineHeight: type.lineHeight };\n};\n\nconst CLAMP_TOKEN = /^(.+)-(\\d+)$/;\n\nconst clampStyle = (\n  theme: Theme,\n  value: string | undefined,\n): StyleDesc | undefined => {\n  const token = pipe(\n    Option.fromNullishOr(value),\n    Option.flatMap(Str.match(CLAMP_TOKEN)),\n  );\n  if (Option.isNone(token)) return undefined;\n  const [, key = \"\", count = \"0\"] = token.value;\n  const role = TEXT_ROLE[key];\n  return role === undefined\n    ? undefined\n    : {\n        whiteSpace: \"normal\",\n        overflow: \"hidden\",\n        height: typeOf(theme, role).lineHeight * Math.max(1, Number(count)),\n      };\n};\n\nconst filledFill = (\n  theme: Theme,\n  role: FilledRole,\n  value: string | undefined,\n): StyleDesc | undefined =>\n  value === undefined\n    ? { backgroundColor: filledHover(theme, role) }\n    : value === \"pressed\"\n      ? { backgroundColor: filledPressed(theme, role) }\n      : undefined;\n\nconst compiler = createTw({\n  utilities: {\n    \"flex-row\": () => ({ flexDirection: \"row\" }),\n    \"justify-start\": () => ({ justifyContent: \"flex-start\" }),\n    \"justify-end\": () => ({ justifyContent: \"flex-end\" }),\n    \"justify-around\": () => ({ justifyContent: \"space-around\" }),\n    \"items-end\": () => ({ alignItems: \"flex-end\" }),\n    \"items-stretch\": () => ({ alignItems: \"stretch\" }),\n    \"items-baseline\": () => ({ alignItems: \"baseline\" }),\n    \"flex-wrap\": () => ({ flexWrap: \"wrap\" }),\n    \"flex-grow\": () => ({ flexGrow: 1 }),\n    \"cursor-text\": () => ({ cursor: \"text\" }),\n\n    \"select-text\": (theme) => ({\n      userSelect: \"text\",\n      selectionColor: colorRole(theme, \"selection\"),\n    }),\n\n    \"text-start\": () => ({ textAlign: textAlignValue(\"start\") }),\n    \"text-end\": () => ({ textAlign: textAlignValue(\"end\") }),\n    \"text-ellipsis\": () => ({ textOverflow: \"ellipsis\" }),\n    \"font-sans\": (theme) => ({ fontFamily: familyOf(theme, \"sans\") }),\n    \"font-mono\": (theme) => ({ fontFamily: familyOf(theme, \"mono\") }),\n    \"h-px\": (theme) => ({ height: borderWidthOf(theme, \"thin\") }),\n    \"w-px\": (theme) => ({ width: borderWidthOf(theme, \"thin\") }),\n\n    \"border-dashed\": () => ({ borderStyle: \"dashed\" }),\n\n    \"aspect-ratio\": (_theme, value) => {\n      const ratio = paintableAspectRatio(\n        value === undefined ? undefined : Number(value),\n      );\n      return ratio === undefined ? undefined : { aspectRatio: ratio };\n    },\n    \"shrink-1\": () => ({ flexShrink: 1 }),\n    \"min-w-full\": () => ({ minWidth: \"100%\" }),\n    \"overflow-x-hidden\": () => ({ overflowX: \"hidden\" }),\n    \"overflow-x-scroll\": () => ({ overflowX: \"scroll\" }),\n    \"overflow-y-hidden\": () => ({ overflowY: \"hidden\" }),\n    \"overflow-y-scroll\": () => ({ overflowY: \"scroll\" }),\n\n    \"text-brand\": (theme) => ({ color: brandText(theme) }),\n\n    \"bg-active\": (theme) => ({ backgroundColor: pressWash(theme) }),\n\n    \"fill-primary\": (theme, value) => filledFill(theme, \"primary\", value),\n    \"fill-secondary\": (theme, value) => filledFill(theme, \"secondary\", value),\n    \"fill-destructive\": (theme, value) =>\n      filledFill(theme, \"destructive\", value),\n\n    \"fill-accent\": (theme, value) => filledFill(theme, \"accent\", value),\n    \"ml-0\": () => ({ marginLeft: 0 }),\n    \"mr-0\": () => ({ marginRight: 0 }),\n    \"mt-0\": () => ({ marginTop: 0 }),\n    \"mb-0\": () => ({ marginBottom: 0 }),\n\n    gap: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined ? undefined : { gap: spaceValue(theme, step) };\n    },\n    p: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { padding: spaceValue(theme, step) };\n    },\n    pl: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { paddingLeft: spaceValue(theme, step) };\n    },\n    pr: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { paddingRight: spaceValue(theme, step) };\n    },\n    pt: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { paddingTop: spaceValue(theme, step) };\n    },\n    pb: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { paddingBottom: spaceValue(theme, step) };\n    },\n    ml: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { marginLeft: spaceValue(theme, step) };\n    },\n    mr: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { marginRight: spaceValue(theme, step) };\n    },\n    mt: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { marginTop: spaceValue(theme, step) };\n    },\n    mb: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { marginBottom: spaceValue(theme, step) };\n    },\n    top: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined ? undefined : { top: spaceValue(theme, step) };\n    },\n    left: (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined ? undefined : { left: spaceValue(theme, step) };\n    },\n    h: (theme, value) => {\n      const size = value === undefined ? undefined : CONTROL_SIZE[value];\n      return size === undefined\n        ? undefined\n        : { height: controlHeightOf(theme, size) };\n    },\n    px: (theme, value) => {\n      const size = value === undefined ? undefined : CONTROL_SIZE[value];\n      const padding =\n        size === undefined ? undefined : controlPaddingOf(theme, size);\n      return padding === undefined\n        ? undefined\n        : { paddingLeft: padding, paddingRight: padding };\n    },\n    size: (theme, value) => {\n      const size = value === undefined ? undefined : CONTROL_SIZE[value];\n      const side =\n        size === undefined ? undefined : controlHeightOf(theme, size);\n      return side === undefined ? undefined : { width: side, height: side };\n    },\n    rounded: (theme, value) => {\n      const step = value === undefined ? undefined : RADIUS_STEP[value];\n      return step === undefined\n        ? undefined\n        : { borderRadius: radiusValue(theme, step) };\n    },\n    type: (theme, value) => {\n      const role = value === undefined ? undefined : TEXT_ROLE[value];\n      return role === undefined ? undefined : roleType(theme, role);\n    },\n    \"type-size\": (theme, value) => {\n      const role = value === undefined ? undefined : TEXT_ROLE[value];\n      return role === undefined ? undefined : roleSize(theme, role);\n    },\n\n    clamp: clampStyle,\n\n    \"size-icon\": (theme, value) => iconSide(theme, value),\n    \"size-dot\": (theme, value) => halfIconSide(theme, value),\n\n    \"size-thumb\": (theme, value) => {\n      const size = controlSize(value);\n      return size === undefined ? undefined : switchBox(size, theme).knob;\n    },\n    \"switch-track\": (theme, value) => {\n      const size = controlSize(value);\n      if (size === undefined) return undefined;\n      const box = switchBox(size, theme);\n      return { width: box.width, height: box.height, borderWidth: box.border };\n    },\n    \"switch-thumb\": (theme, value) => {\n      const size = controlSize(value);\n      if (size === undefined) return undefined;\n      const box = switchBox(size, theme);\n      return {\n        top: (box.height - box.border * 2 - box.knob) / 2,\n        left: switchThumbOffset(\n          { state: \"unchecked\", size, accent: \"primary\" },\n          theme,\n        ),\n      };\n    },\n    \"switch-thumb-on\": (theme, value) => {\n      const size = controlSize(value);\n      return size === undefined\n        ? undefined\n        : {\n            left: switchThumbOffset(\n              { state: \"checked\", size, accent: \"primary\" },\n              theme,\n            ),\n          };\n    },\n    \"slider-thumb\": (theme, value) => {\n      const side = iconSide(theme, value);\n      if (side === undefined) return undefined;\n      const half = Math.round(side / 2);\n      return {\n        width: side,\n        height: side,\n        marginLeft: -half,\n        marginRight: -half,\n        borderWidth: borderWidthOf(theme, \"strong\"),\n      };\n    },\n\n    \"slider-thumb-v\": (theme, value) => {\n      const side = iconSide(theme, value);\n      if (side === undefined) return undefined;\n      const half = Math.round(side / 2);\n      return {\n        width: side,\n        height: side,\n        marginTop: -half,\n        marginBottom: -half,\n        borderWidth: borderWidthOf(theme, \"strong\"),\n      };\n    },\n    \"px-inset\": (theme, value) => {\n      const half = halfIconSide(theme, value);\n      return half === undefined\n        ? undefined\n        : { paddingLeft: half, paddingRight: half };\n    },\n    \"py-inset\": (theme, value) => {\n      const half = halfIconSide(theme, value);\n      return half === undefined\n        ? undefined\n        : { paddingTop: half, paddingBottom: half };\n    },\n\n    \"w-control\": (theme, value) => {\n      const size = controlSize(value);\n      return size === undefined\n        ? undefined\n        : { width: controlHeightOf(theme, size) };\n    },\n    \"w-stepper-column\": (theme) => ({\n      width: iconSideOf(theme, \"md\") + spaceValue(theme, \"sm\"),\n    }),\n\n    \"min-target\": () => ({\n      minWidth: targetSize.minimum,\n      minHeight: targetSize.minimum,\n    }),\n    \"h-space\": (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { height: spaceValue(theme, step) };\n    },\n    \"w-space\": (theme, value) => {\n      const step = spacingStep(value);\n      return step === undefined\n        ? undefined\n        : { width: spaceValue(theme, step) };\n    },\n  },\n});\n\nexport const recipeTw: TwCompiler = {\n  ...compiler,\n  classesToStyle: (className, theme, state) => {\n    const result = compiler.classesToStyle(className, theme, state);\n    if (theme.interaction !== \"appkit\") return result;\n    const { hover: _pointer, ...style } = result.style;\n    return { ...result, style };\n  },\n};\n"
    },
    {
      "path": "packages/primitives/src/scroll-area.ts",
      "target": "@ui/gpuixui/primitives/scroll-area.ts",
      "type": "registry:lib",
      "content": "export { ScrollArea, ScrollAreaViewport } from \"./components/ScrollArea.js\";\nexport type {\n  ScrollAreaProps,\n  ScrollAreaViewportProps,\n} from \"./components/ScrollArea.js\";\n"
    },
    {
      "path": "packages/primitives/src/select.ts",
      "target": "@ui/gpuixui/primitives/select.ts",
      "type": "registry:lib",
      "content": "export {\n  Select,\n  selectOptions,\n  selectValueLabel,\n} from \"./components/Select.js\";\nexport type {\n  SelectEntry,\n  SelectOption,\n  SelectOptionGroup,\n  SelectProps,\n} from \"./components/Select.js\";\n"
    },
    {
      "path": "packages/primitives/src/separator.ts",
      "target": "@ui/gpuixui/primitives/separator.ts",
      "type": "registry:lib",
      "content": "export { Separator } from \"./components/Separator.js\";\nexport type { SeparatorProps } from \"./components/Separator.js\";\n"
    },
    {
      "path": "packages/primitives/src/skeleton.ts",
      "target": "@ui/gpuixui/primitives/skeleton.ts",
      "type": "registry:lib",
      "content": "export { Skeleton, renderSkeleton } from \"./components/Skeleton.js\";\nexport type { SkeletonProps } from \"./components/Skeleton.js\";\nexport type { SkeletonSpec } from \"./core/loading.js\";\n"
    },
    {
      "path": "packages/primitives/src/slider.ts",
      "target": "@ui/gpuixui/primitives/slider.ts",
      "type": "registry:lib",
      "content": "export {\n  RangeSlider,\n  Slider,\n  SliderIndicator,\n  SliderThumb,\n  SliderTrack,\n  sliderRange,\n  sliderValueText,\n} from \"./components/Slider.js\";\nexport type {\n  RangeSliderProps,\n  SliderIndicatorProps,\n  SliderProps,\n  SliderState,\n  SliderThumbProps,\n  SliderTrackProps,\n} from \"./components/Slider.js\";\nexport type { SliderOrientation } from \"./core/style.js\";\n"
    },
    {
      "path": "packages/primitives/src/spinner.ts",
      "target": "@ui/gpuixui/primitives/spinner.ts",
      "type": "registry:lib",
      "content": "export { Spinner, renderSpinner } from \"./components/Spinner.js\";\nexport type { SpinnerProps } from \"./components/Spinner.js\";\nexport {\n  SPINNER_ARC_TICKS,\n  SPINNER_RING_TICKS,\n  spinnerTicks,\n} from \"./core/loading.js\";\nexport type { SpinnerAccent, SpinnerTick } from \"./core/loading.js\";\n"
    },
    {
      "path": "packages/primitives/src/toggle-group.ts",
      "target": "@ui/gpuixui/primitives/toggle-group.ts",
      "type": "registry:lib",
      "content": "export {\n  ToggleGroup,\n  ToggleGroupItem,\n  toggleGroupValue,\n  toggleGroupValues,\n} from \"./components/ToggleGroup.js\";\nexport type {\n  MultipleToggleGroupProps,\n  SingleToggleGroupProps,\n  ToggleGroupItemProps,\n  ToggleGroupMode,\n  ToggleGroupOption,\n  ToggleGroupProps,\n  ToggleGroupState,\n  ToggleItemState,\n} from \"./components/ToggleGroup.js\";\n"
    },
    {
      "path": "packages/primitives/src/toggle.ts",
      "target": "@ui/gpuixui/primitives/toggle.ts",
      "type": "registry:lib",
      "content": "export { Toggle, pressedValueText } from \"./components/Toggle.js\";\nexport type { ToggleProps, ToggleState } from \"./components/Toggle.js\";\nexport type { ToggleVariant } from \"./core/variants.js\";\n"
    }
  ],
  "docs": "[Runtime: primitives](https://gpuixui.sudokw.com/registry) — 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": "/registry",
    "kind": "runtime",
    "runtime": "gpuix",
    "registry": "gpuixui"
  }
}