{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "property-syntax-editor",
  "title": "@property Syntax Editor",
  "description": "Ridiculously typed editor for the CSS @property syntax: descriptor. The namesake types one CSS string with another — InitialValueLiteral<Syntax, V> checks an initial-value against the validated syntax, so cssProperty(\"<length>\", \"red\") is a compile error. A chip builder sits beside a live green/red demo.",
  "dependencies": [],
  "registryDependencies": [
    "ridiculous-type-kit",
    "color-picker",
    "button",
    "popover",
    "input"
  ],
  "files": [
    {
      "path": "src/components/ui/property-syntax-editor/index.ts",
      "content": "export type {\n  InitialValueFieldProps,\n  MiniSelectProps,\n  PropertySyntaxEditorPanelProps,\n  PropertySyntaxEditorProps,\n  SyntaxChipBuilderProps,\n} from \"./property-syntax-editor\"\nexport {\n  InitialValueField,\n  MiniSelect,\n  PropertySyntaxEditor,\n  PropertySyntaxEditorPanel,\n  SyntaxChipBuilder,\n} from \"./property-syntax-editor\"\nexport {\n  dataTypeNames,\n  defaultInitialValue,\n  defaultSyntax,\n  formatSyntax,\n  matchesSyntax,\n  parseSyntax,\n} from \"./property-syntax-editor.helpers\"\nexport type {\n  ComponentsOf,\n  DataTypeName,\n  InitialValueLiteral,\n  MultiplierToken,\n  PropertySyntaxState,\n  SatisfiesBase,\n  SyntaxComponent,\n  SyntaxLiteral,\n  SyntaxString,\n} from \"./property-syntax-editor.types\"\nexport {\n  cssProperty,\n  cssSyntax,\n} from \"./property-syntax-editor.types\"\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/index.ts"
    },
    {
      "path": "src/components/ui/property-syntax-editor/property-syntax-editor.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { cn } from \"@/lib/utils\"\nimport { InitialValueField } from \"./initial-value-field\"\nimport {\n  defaultInitialValue,\n  formatSyntax,\n  parseSyntax,\n} from \"./property-syntax-editor.helpers\"\nimport type {\n  PropertySyntaxState,\n  SyntaxString,\n} from \"./property-syntax-editor.types\"\nimport { SyntaxChipBuilder } from \"./syntax-chip-builder\"\n\n// Re-export the public sub-components + their prop types so consumers (and the\n// barrel) can import them from `./property-syntax-editor`.\nexport type { InitialValueFieldProps } from \"./initial-value-field\"\nexport { InitialValueField } from \"./initial-value-field\"\nexport type { MiniSelectProps } from \"./mini-select\"\nexport { MiniSelect } from \"./mini-select\"\nexport type { SyntaxChipBuilderProps } from \"./syntax-chip-builder\"\nexport { SyntaxChipBuilder } from \"./syntax-chip-builder\"\n\n// ---------------------------------------------------------------------------\n// value string ⇄ editor state\n// ---------------------------------------------------------------------------\n\nfunction deriveState(\n  value: string,\n  prev?: PropertySyntaxState,\n): PropertySyntaxState {\n  const { universal, components } = parseSyntax(value)\n  return {\n    universal,\n    components,\n    initialValue: prev?.initialValue ?? defaultInitialValue(value),\n    inherits: prev?.inherits ?? false,\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface PropertySyntaxEditorPanelProps {\n  value: SyntaxString | (string & {})\n  onChange: (value: SyntaxString) => void\n  className?: string\n  \"aria-label\"?: string\n}\n\nexport interface PropertySyntaxEditorProps\n  extends PropertySyntaxEditorPanelProps {}\n\n// ---------------------------------------------------------------------------\n// PropertySyntaxEditor — popover-wrapped\n// ---------------------------------------------------------------------------\n\nexport function PropertySyntaxEditor(props: PropertySyntaxEditorProps) {\n  const {\n    value,\n    className,\n    \"aria-label\": ariaLabel = \"Edit a CSS @property syntax descriptor\",\n  } = props\n\n  return (\n    <Popover>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"outline\"\n          className={cn(\"h-9 gap-2 px-3 font-mono\", className)}\n          aria-label={ariaLabel}\n        >\n          <span className=\"text-[10px] text-muted-foreground uppercase\">\n            syntax\n          </span>\n          <span className=\"max-w-[220px] truncate text-xs\">{value}</span>\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-auto p-0\" align=\"start\">\n        <PropertySyntaxEditorPanel {...props} />\n      </PopoverContent>\n    </Popover>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// PropertySyntaxEditorPanel — inline\n// ---------------------------------------------------------------------------\n\nexport function PropertySyntaxEditorPanel({\n  value,\n  onChange,\n  className,\n  \"aria-label\": ariaLabel = \"CSS @property syntax editor\",\n}: PropertySyntaxEditorPanelProps) {\n  const [state, setState] = useState<PropertySyntaxState>(() =>\n    deriveState(String(value)),\n  )\n  const lastEmittedRef = useRef<string | null>(null)\n\n  // Resync from an external value (skip our own emits).\n  useEffect(() => {\n    if (value === lastEmittedRef.current) return\n    setState((prev) => deriveState(String(value), prev))\n  }, [value])\n\n  const commit = (next: PropertySyntaxState) => {\n    setState(next)\n    const str = formatSyntax(next.universal, next.components)\n    lastEmittedRef.current = str\n    onChange(str as SyntaxString)\n  }\n\n  const liveString = formatSyntax(state.universal, state.components)\n\n  return (\n    <fieldset\n      className={cn(\n        \"m-0 w-[460px] space-y-3 border-0 bg-background p-3\",\n        className,\n      )}\n      aria-label={ariaLabel}\n    >\n      <SyntaxChipBuilder\n        universal={state.universal}\n        components={state.components}\n        onChange={({ universal, components }) =>\n          commit({ ...state, universal, components })\n        }\n      />\n\n      <code className=\"block overflow-x-auto rounded bg-muted/50 px-2 py-1.5 font-mono text-foreground text-xs\">\n        {liveString || \" \"}\n      </code>\n\n      <div className=\"border-input border-t pt-3\">\n        <p className=\"mb-2 font-mono text-[10px] text-muted-foreground uppercase\">\n          initial-value (live check)\n        </p>\n        <InitialValueField\n          syntax={liveString}\n          value={state.initialValue}\n          inherits={state.inherits}\n          onValueChange={(initialValue) => setState({ ...state, initialValue })}\n          onInheritsChange={(inherits) => setState({ ...state, inherits })}\n        />\n      </div>\n    </fieldset>\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/property-syntax-editor.tsx"
    },
    {
      "path": "src/components/ui/property-syntax-editor/property-syntax-editor.types.ts",
      "content": "// =====================================================================\n// property-syntax-editor.types.ts — ridiculously typed CSS @property\n// `syntax:` descriptor, with the crown-jewel dependent validator.\n//\n//   • SyntaxLiteral<S> — the `<syntax>` meta-grammar: \"*\" | a `|`-separated\n//     list of `<data-type>` / literal-ident components, each with an optional\n//     single `+` (space list) or `#` (comma list) multiplier.\n//   • InitialValueLiteral<Syntax, V> — ONE CSS STRING TYPING ANOTHER: a\n//     candidate `initial-value` V is checked AGAINST a validated `Syntax`.\n//     `cssProperty(syntax, initialValue)` type-checks only when they agree.\n//\n// `<color>` defers to color-picker's ColorLiteral (the honest cross-component\n// showcase). The `|` combinator never nests in a @property syntax descriptor,\n// so a flat local SplitPipe replaces the kit's (unexported) SplitTopLevel.\n//\n// Spec: docs/superpowers/specs/2026-06-19-property-syntax-editor-design.md\n// =====================================================================\n\nimport type { ColorLiteral } from \"@/components/ui/color-picker\"\nimport type {\n  Digit,\n  IsAngle,\n  IsLength,\n  IsNumber,\n  IsPercentage,\n  IsResolution,\n  IsTime,\n  KeepIf,\n  Letter,\n  NonEmptyAllChars,\n  Or,\n  SplitByComma,\n  SplitBySpace,\n  Trim,\n} from \"@/lib/ridiculous-type-kit\"\n\n// ---------------------------------------------------------------------------\n// data types + local splitters\n// ---------------------------------------------------------------------------\n\n/** The data-type names a `@property` syntax descriptor may reference. */\nexport type DataTypeName =\n  | \"length\"\n  | \"number\"\n  | \"percentage\"\n  | \"length-percentage\"\n  | \"color\"\n  | \"integer\"\n  | \"angle\"\n  | \"time\"\n  | \"resolution\"\n  | \"image\"\n  | \"url\"\n  | \"transform-function\"\n  | \"transform-list\"\n  | \"custom-ident\"\n\n/** A single `+`/`#` multiplier (or none). */\nexport type MultiplierToken = \"\" | \"+\" | \"#\"\n\n/** Split a descriptor on its top-level `|` (no nesting in this grammar). */\ntype SplitPipe<\n  S extends string,\n  Acc extends string[] = [],\n> = S extends `${infer H}|${infer R}`\n  ? SplitPipe<R, [...Acc, Trim<H>]>\n  : [...Acc, Trim<S>]\n\n/** Peel a single trailing `+`/`#` multiplier off a component. */\ntype StripMult<C extends string> = C extends `${infer Base}#`\n  ? { base: Trim<Base>; mult: \"#\" }\n  : C extends `${infer Base}+`\n    ? { base: Trim<Base>; mult: \"+\" }\n    : { base: Trim<C>; mult: \"\" }\n\ntype IdentChar = Letter | Digit | \"-\" | \"_\"\n\n/** A component base is either `<known-data-type>` or a (lenient) literal ident. */\ntype IsTypeOrIdent<Base extends string> = Base extends `<${infer T}>`\n  ? T extends DataTypeName\n    ? true\n    : false\n  : NonEmptyAllChars<Base, IdentChar>\n\n// ---------------------------------------------------------------------------\n// SyntaxLiteral<S>\n// ---------------------------------------------------------------------------\n\ntype ValidComponent<C extends string> =\n  StripMult<C> extends {\n    base: infer B extends string\n  }\n    ? IsTypeOrIdent<B>\n    : false\n\ntype AllValidComponents<Comps extends string[]> = Comps extends [\n  infer H extends string,\n  ...infer R extends string[],\n]\n  ? ValidComponent<H> extends true\n    ? AllValidComponents<R>\n    : false\n  : true\n\n/** Strict validator for a `@property` `syntax:` value. `S` or `never`. */\nexport type SyntaxLiteral<S extends string> =\n  Trim<S> extends \"*\" ? S : KeepIf<AllValidComponents<SplitPipe<S>>, S>\n\n// ---------------------------------------------------------------------------\n// InitialValueLiteral<Syntax, V> — the dependent validator\n// ---------------------------------------------------------------------------\n\ntype IsColor<Tok extends string> = [ColorLiteral<Tok>] extends [never]\n  ? false\n  : true\n\n/** Does a single token satisfy a single (multiplier-stripped) base? */\ntype SatisfiesBase<\n  Tok extends string,\n  Base extends string,\n> = Base extends `<${infer T}>`\n  ? T extends \"length\"\n    ? IsLength<Tok>\n    : T extends \"number\"\n      ? IsNumber<Tok>\n      : T extends \"integer\"\n        ? IsNumber<Tok>\n        : T extends \"percentage\"\n          ? IsPercentage<Tok>\n          : T extends \"length-percentage\"\n            ? Or<IsLength<Tok>, IsPercentage<Tok>>\n            : T extends \"angle\"\n              ? IsAngle<Tok>\n              : T extends \"time\"\n                ? IsTime<Tok>\n                : T extends \"resolution\"\n                  ? IsResolution<Tok>\n                  : T extends \"color\"\n                    ? IsColor<Tok>\n                    : // image / url / transform-* / custom-ident → lenient\n                      true\n  : // literal ident → exact match\n    Tok extends Base\n    ? true\n    : false\n\ntype AllSatisfy<Toks extends string[], Base extends string> = Toks extends [\n  infer H extends string,\n  ...infer R extends string[],\n]\n  ? SatisfiesBase<H, Base> extends true\n    ? AllSatisfy<R, Base>\n    : false\n  : true\n\ntype NonEmptyAll<Toks extends string[], Base extends string> = Toks extends []\n  ? false\n  : AllSatisfy<Toks, Base>\n\ntype SatisfiesComp<V extends string, Comp extends string> =\n  StripMult<Comp> extends {\n    base: infer B extends string\n    mult: infer M extends string\n  }\n    ? M extends \"+\"\n      ? NonEmptyAll<SplitBySpace<V>, B>\n      : M extends \"#\"\n        ? NonEmptyAll<SplitByComma<V>, B>\n        : SatisfiesBase<Trim<V>, B>\n    : false\n\ntype MatchesAny<V extends string, Comps extends string[]> = Comps extends [\n  infer H extends string,\n  ...infer R extends string[],\n]\n  ? SatisfiesComp<V, H> extends true\n    ? true\n    : MatchesAny<V, R>\n  : false\n\n/** Dependent validator: a candidate `initial-value` V vs a `Syntax`. */\nexport type InitialValueLiteral<Syntax extends string, V extends string> =\n  Trim<Syntax> extends \"*\" ? V : KeepIf<MatchesAny<V, SplitPipe<Syntax>>, V>\n\n/** Exported for advanced use (custom serialization / extension). */\nexport type { SatisfiesBase }\n\n// ---------------------------------------------------------------------------\n// Call-site helpers\n// ---------------------------------------------------------------------------\n\nexport const cssSyntax = <S extends string>(value: S & SyntaxLiteral<S>): S =>\n  value\n\n/**\n * The crown jewel: type-checks ONLY when `initialValue` satisfies the\n * declared `syntax` — one CSS string constraining another's type.\n */\nexport const cssProperty = <Syn extends string, Init extends string>(\n  syntax: Syn & SyntaxLiteral<Syn>,\n  initialValue: Init & InitialValueLiteral<Syn, Init>,\n): { syntax: Syn; initialValue: Init } => ({ syntax, initialValue })\n\n// ---------------------------------------------------------------------------\n// IntelliSense suggestion strings\n// ---------------------------------------------------------------------------\n\nexport type SyntaxString =\n  | \"*\"\n  | `<${DataTypeName}>`\n  | `<${DataTypeName}>+`\n  | `<${DataTypeName}>#`\n  | (string & {})\n\n// ---------------------------------------------------------------------------\n// Utility + state\n// ---------------------------------------------------------------------------\n\n/** The component strings of a syntax descriptor (split on `|`). */\nexport type ComponentsOf<S extends string> = SplitPipe<S>\n\nexport interface SyntaxComponent {\n  base: string\n  isType: boolean\n  multiplier: MultiplierToken\n}\n\nexport interface PropertySyntaxState {\n  universal: boolean\n  components: SyntaxComponent[]\n  initialValue: string\n  inherits: boolean\n}\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/property-syntax-editor.types.ts"
    },
    {
      "path": "src/components/ui/property-syntax-editor/property-syntax-editor.helpers.ts",
      "content": "// =====================================================================\n// property-syntax-editor.helpers.ts\n//\n// Pure runtime parse / format / match for the CSS `@property` `syntax:`\n// descriptor. This is the SUPERSET of the strict type tier: `parseSyntax`\n// classifies the structure (universal `*`, the `|` alternation, each\n// component's `<data-type>`-or-ident base + optional single `+`/`#`\n// multiplier) and surfaces an `error`; `matchesSyntax` is the RUNTIME MIRROR\n// of the dependent type `InitialValueLiteral` — it decides whether a candidate\n// `initial-value` satisfies a syntax string (mirrors query-builder.helpers.ts).\n//\n// `DATA_TYPE_TABLE` below is the runtime twin of the type `SatisfiesBase` in\n// property-syntax-editor.types.ts — separately authored, reviewed together (the\n// query-builder precedent). The same example values appear in both the type-\n// test and the format-test, so any drift fails a test.\n//\n// `<color>` defers to color-picker's `isColorString` (the runtime half of the\n// `ColorLiteral` cross-component showcase) — so color-picker forms (hex / rgb /\n// hsl / oklch / oklab / hwb) match, but a named color like `red` does not.\n//\n// Spec: docs/superpowers/specs/2026-06-19-property-syntax-editor-design.md §4\n// =====================================================================\n\nimport { isColorString } from \"@/components/ui/color-picker\"\nimport type {\n  DataTypeName,\n  SyntaxComponent,\n} from \"./property-syntax-editor.types\"\n\n// ---------------------------------------------------------------------------\n// DATA_TYPE_TABLE — one row per `<data-type>`. `test` is the runtime predicate\n// twin of the type `SatisfiesBase` for that base. Idents and `*` are handled\n// outside the table (exact-match / universal).\n// ---------------------------------------------------------------------------\n\n// Number grammar mirrored from the kit's `IsNumber`: an optional `+`/`-` sign,\n// a required integer part, and an optional single `.`-fraction (digits both\n// sides). No bare `.5`, no trailing `1.` — exactly what the type accepts.\nconst NUMBER_RE = /^[+-]?\\d+(?:\\.\\d+)?$/\n\n// Integer is the runtime-stricter `<integer>` check (spec A4): sign + digits,\n// no fraction.\nconst INTEGER_RE = /^[+-]?\\d+$/\n\n// Unit suffixes, mirrored 1:1 from the kit's dimension unit lists.\nconst LENGTH_UNITS = [\n  \"cqmin\",\n  \"cqmax\",\n  \"vmin\",\n  \"vmax\",\n  \"svw\",\n  \"svh\",\n  \"svi\",\n  \"svb\",\n  \"lvw\",\n  \"lvh\",\n  \"lvi\",\n  \"lvb\",\n  \"dvw\",\n  \"dvh\",\n  \"dvi\",\n  \"dvb\",\n  \"cqw\",\n  \"cqh\",\n  \"cqi\",\n  \"cqb\",\n  \"rlh\",\n  \"rem\",\n  \"cap\",\n  \"rex\",\n  \"px\",\n  \"em\",\n  \"ex\",\n  \"ch\",\n  \"ic\",\n  \"lh\",\n  \"vw\",\n  \"vh\",\n  \"vi\",\n  \"vb\",\n  \"cm\",\n  \"mm\",\n  \"in\",\n  \"pt\",\n  \"pc\",\n  \"Q\",\n] as const\nconst ANGLE_UNITS = [\"turn\", \"grad\", \"deg\", \"rad\"] as const\nconst TIME_UNITS = [\"ms\", \"s\"] as const\nconst RESOLUTION_UNITS = [\"dpcm\", \"dppx\", \"dpi\", \"x\"] as const\n\n/** Number with one of the given unit suffixes (the kit's `HasUnit`). */\nfunction hasUnit(value: string, units: readonly string[]): boolean {\n  for (const u of units) {\n    if (value.endsWith(u)) {\n      const n = value.slice(0, value.length - u.length).trim()\n      if (NUMBER_RE.test(n)) return true\n    }\n  }\n  return false\n}\n\nconst isNumber = (v: string): boolean => NUMBER_RE.test(v.trim())\nconst isInteger = (v: string): boolean => INTEGER_RE.test(v.trim())\nconst isLength = (v: string): boolean => hasUnit(v.trim(), LENGTH_UNITS)\nconst isAngle = (v: string): boolean => hasUnit(v.trim(), ANGLE_UNITS)\nconst isTime = (v: string): boolean => hasUnit(v.trim(), TIME_UNITS)\nconst isResolution = (v: string): boolean => hasUnit(v.trim(), RESOLUTION_UNITS)\nconst isPercentage = (v: string): boolean => {\n  const t = v.trim()\n  return t.endsWith(\"%\") && NUMBER_RE.test(t.slice(0, -1).trim())\n}\n\ninterface DataTypeRow {\n  /** A representative default `initial-value` for this type. */\n  sample: string\n  /** Runtime predicate; mirror of the type `SatisfiesBase` for this base. */\n  test: (token: string) => boolean\n}\n\n/**\n * The data-type table — the palette vocabulary + the per-type runtime\n * predicate. `image` / `url` / `transform-function` / `transform-list` are\n * lenient (any non-empty token), mirroring the type tier (spec A5).\n */\nconst DATA_TYPE_TABLE: Record<DataTypeName, DataTypeRow> = {\n  length: { sample: \"0px\", test: isLength },\n  number: { sample: \"0\", test: isNumber },\n  percentage: { sample: \"0%\", test: isPercentage },\n  \"length-percentage\": {\n    sample: \"0px\",\n    test: (t) => isLength(t) || isPercentage(t),\n  },\n  color: { sample: \"#000000\", test: (t) => isColorString(t.trim()) },\n  integer: { sample: \"0\", test: isInteger },\n  angle: { sample: \"0deg\", test: isAngle },\n  time: { sample: \"0s\", test: isTime },\n  resolution: { sample: \"1dppx\", test: isResolution },\n  image: { sample: \"url(image.png)\", test: (t) => t.trim().length > 0 },\n  url: { sample: \"url(image.png)\", test: (t) => t.trim().length > 0 },\n  \"transform-function\": {\n    sample: \"translateX(0px)\",\n    test: (t) => t.trim().length > 0,\n  },\n  \"transform-list\": {\n    sample: \"translateX(0px)\",\n    test: (t) => t.trim().length > 0,\n  },\n  \"custom-ident\": { sample: \"none\", test: (t) => t.trim().length > 0 },\n}\n\nconst DATA_TYPE_ORDER: readonly DataTypeName[] = [\n  \"length\",\n  \"number\",\n  \"percentage\",\n  \"length-percentage\",\n  \"color\",\n  \"integer\",\n  \"angle\",\n  \"time\",\n  \"resolution\",\n  \"image\",\n  \"url\",\n  \"transform-function\",\n  \"transform-list\",\n  \"custom-ident\",\n]\n\n/** The palette options — every known `<data-type>` name. */\nexport function dataTypeNames(): readonly string[] {\n  return DATA_TYPE_ORDER\n}\n\nfunction isKnownDataType(name: string): name is DataTypeName {\n  return Object.hasOwn(DATA_TYPE_TABLE, name)\n}\n\nconst IDENT_RE = /^[A-Za-z0-9_-]+$/\n\n// ---------------------------------------------------------------------------\n// parseSyntax — string → { universal, components, error }\n// ---------------------------------------------------------------------------\n\n/** Peel a single trailing `+`/`#` multiplier; a second one is an error. */\nfunction parseComponent(\n  raw: string,\n): { component: SyntaxComponent; error: null } | { error: string } {\n  const c = raw.trim()\n  if (c === \"\") return { error: \"empty component\" }\n\n  let multiplier: SyntaxComponent[\"multiplier\"] = \"\"\n  let base = c\n  const last = c[c.length - 1]\n  if (last === \"+\" || last === \"#\") {\n    multiplier = last\n    base = c.slice(0, -1).trim()\n  }\n  if (base === \"\") return { error: \"a multiplier needs a base\" }\n  // a second trailing multiplier (e.g. `<length>++`)\n  if (base.endsWith(\"+\") || base.endsWith(\"#\")) {\n    return { error: `double multiplier: ${c}` }\n  }\n\n  // a `<data-type>` base\n  if (base.startsWith(\"<\") || base.endsWith(\">\")) {\n    const m = /^<([^<>]*)>$/.exec(base)\n    if (m === null) return { error: `malformed type: ${base}` }\n    const name = m[1].trim()\n    if (name === \"\") return { error: \"empty data type `<>`\" }\n    if (!isKnownDataType(name)) return { error: `unknown data type: ${name}` }\n    return {\n      component: { base, isType: true, multiplier },\n      error: null,\n    }\n  }\n\n  // a literal ident base\n  if (!IDENT_RE.test(base)) return { error: `invalid ident: ${base}` }\n  return { component: { base, isType: false, multiplier }, error: null }\n}\n\n/**\n * Parse a `@property` `syntax:` descriptor into `{ universal, components,\n * error }`. The universal `*` yields `{ universal: true, components: [] }`;\n * otherwise the `|`-separated components are each classified as a known\n * `<data-type>` or a literal ident, with an optional single `+`/`#`\n * multiplier. Components parsed before an error are kept (so the UI can show\n * what was typed); `error` is `null` only on a fully valid descriptor.\n */\nexport function parseSyntax(src: string): {\n  universal: boolean\n  components: SyntaxComponent[]\n  error: string | null\n} {\n  const trimmed = src.trim()\n  if (trimmed === \"\") {\n    return { universal: false, components: [], error: \"empty syntax\" }\n  }\n  if (trimmed === \"*\") {\n    return { universal: true, components: [], error: null }\n  }\n\n  const parts = trimmed.split(\"|\")\n  const components: SyntaxComponent[] = []\n  let error: string | null = null\n  for (const part of parts) {\n    const result = parseComponent(part)\n    if (result.error !== null) {\n      error = result.error\n      break\n    }\n    components.push(result.component)\n  }\n  return { universal: false, components, error }\n}\n\n// ---------------------------------------------------------------------------\n// formatSyntax — (universal, components) → string\n// ---------------------------------------------------------------------------\n\n/** Serialize one component back to `base[+|#]`. */\nfunction formatComponent(c: SyntaxComponent): string {\n  return `${c.base}${c.multiplier}`\n}\n\n/**\n * Serialize the editor state to a syntax string. The universal `*` ignores the\n * components; otherwise the components join with ` | `. No components yields\n * the empty string.\n */\nexport function formatSyntax(\n  universal: boolean,\n  components: SyntaxComponent[],\n): string {\n  if (universal) return \"*\"\n  return components.map(formatComponent).join(\" | \")\n}\n\n// ---------------------------------------------------------------------------\n// matchesSyntax — the runtime mirror of InitialValueLiteral\n// ---------------------------------------------------------------------------\n\n/** Does a single token satisfy a single (multiplier-stripped) base? */\nfunction satisfiesBase(token: string, base: string): boolean {\n  const m = /^<([^<>]*)>$/.exec(base)\n  if (m !== null) {\n    const name = m[1].trim()\n    if (!isKnownDataType(name)) return false\n    return DATA_TYPE_TABLE[name].test(token)\n  }\n  // literal ident → exact match\n  return token.trim() === base\n}\n\n/** Does a value satisfy a single component (honoring its multiplier)? */\nfunction satisfiesComponent(value: string, c: SyntaxComponent): boolean {\n  if (c.multiplier === \"+\") {\n    const tokens = value\n      .trim()\n      .split(/\\s+/)\n      .filter((t) => t.length > 0)\n    if (tokens.length === 0) return false\n    return tokens.every((t) => satisfiesBase(t, c.base))\n  }\n  if (c.multiplier === \"#\") {\n    const tokens = value\n      .split(\",\")\n      .map((t) => t.trim())\n      .filter((t) => t.length > 0)\n    if (tokens.length === 0) return false\n    return tokens.every((t) => satisfiesBase(t, c.base))\n  }\n  return satisfiesBase(value.trim(), c.base)\n}\n\n/**\n * Whether a candidate `initial-value` satisfies a `syntax` descriptor — the\n * runtime mirror of the dependent type `InitialValueLiteral`. The universal\n * `*` accepts anything; otherwise the value must satisfy ANY `|` alternative.\n * An unparseable / errored syntax never matches.\n */\nexport function matchesSyntax(syntax: string, value: string): boolean {\n  const parsed = parseSyntax(syntax)\n  if (parsed.error !== null) return false\n  if (parsed.universal) return true\n  return parsed.components.some((c) => satisfiesComponent(value, c))\n}\n\n// ---------------------------------------------------------------------------\n// defaults\n// ---------------------------------------------------------------------------\n\n/** A sensible seed syntax descriptor. */\nexport function defaultSyntax(): string {\n  return \"<length>\"\n}\n\n/**\n * A representative `initial-value` for a syntax string — the sample of the\n * first component's data type, the ident itself for a literal, or `0` for the\n * universal `*` / an unparseable syntax. Always a value the runtime accepts\n * (when the syntax is valid).\n */\nexport function defaultInitialValue(syntax: string): string {\n  const parsed = parseSyntax(syntax)\n  if (parsed.universal) return \"0\"\n  const first = parsed.components[0]\n  if (first === undefined) return \"0\"\n  if (!first.isType) return first.base\n  const m = /^<([^<>]*)>$/.exec(first.base)\n  const name = m?.[1]?.trim() ?? \"\"\n  if (isKnownDataType(name)) {\n    const { sample } = DATA_TYPE_TABLE[name]\n    // a `#` (comma) or `+` (space) multiplier still satisfies with one token\n    return sample\n  }\n  return \"0\"\n}\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/property-syntax-editor.helpers.ts"
    },
    {
      "path": "src/components/ui/property-syntax-editor/syntax-chip-builder.tsx",
      "content": "\"use client\"\n\nimport { useState } from \"react\"\nimport { cn } from \"@/lib/utils\"\nimport { dataTypeNames } from \"./property-syntax-editor.helpers\"\nimport type {\n  MultiplierToken,\n  SyntaxComponent,\n} from \"./property-syntax-editor.types\"\n\n// ---------------------------------------------------------------------------\n// SyntaxChipBuilder (public) — the `@property` `syntax:` chip editor. A palette\n// of `<data-type>` buttons + an \"add literal ident\" input append OR'd\n// components; each chosen chip is a pill with a none / `+` / `#` multiplier\n// segmented toggle, a reorder pair, and a remove. Chips render with a visible\n// `|` between them. A `*` (universal) switch is exclusive — flipping it on\n// clears the chips; flipping it off restores an editable component. The\n// container owns the state; this edits it immutably.\n// ---------------------------------------------------------------------------\n\nconst MULTIPLIERS: readonly { token: MultiplierToken; label: string }[] = [\n  { token: \"\", label: \"·\" },\n  { token: \"+\", label: \"+\" },\n  { token: \"#\", label: \"#\" },\n]\n\nconst IDENT_RE = /^[A-Za-z0-9_-]+$/\n\nexport interface SyntaxChipBuilderProps {\n  universal: boolean\n  components: SyntaxComponent[]\n  onChange: (next: {\n    universal: boolean\n    components: SyntaxComponent[]\n  }) => void\n  className?: string\n}\n\nexport function SyntaxChipBuilder({\n  universal,\n  components,\n  onChange,\n  className,\n}: SyntaxChipBuilderProps) {\n  const [ident, setIdent] = useState(\"\")\n\n  const updateAt = (index: number, c: SyntaxComponent) =>\n    onChange({\n      universal: false,\n      components: components.map((it, i) => (i === index ? c : it)),\n    })\n  const removeAt = (index: number) =>\n    onChange({\n      universal: false,\n      components: components.filter((_, i) => i !== index),\n    })\n  const move = (index: number, dir: -1 | 1) => {\n    const target = index + dir\n    if (target < 0 || target >= components.length) return\n    const next = [...components]\n    ;[next[index], next[target]] = [next[target], next[index]]\n    onChange({ universal: false, components: next })\n  }\n  const addType = (name: string) =>\n    onChange({\n      universal: false,\n      components: [\n        ...components,\n        { base: `<${name}>`, isType: true, multiplier: \"\" },\n      ],\n    })\n  const addIdent = () => {\n    const v = ident.trim()\n    if (v === \"\" || !IDENT_RE.test(v)) return\n    onChange({\n      universal: false,\n      components: [...components, { base: v, isType: false, multiplier: \"\" }],\n    })\n    setIdent(\"\")\n  }\n  const toggleUniversal = (next: boolean) => {\n    if (next) onChange({ universal: true, components: [] })\n    else\n      onChange({\n        universal: false,\n        components: [{ base: \"<length>\", isType: true, multiplier: \"\" }],\n      })\n  }\n\n  return (\n    <div className={cn(\"space-y-3\", className)}>\n      <label className=\"flex h-8 w-fit items-center gap-1.5 rounded-md border border-input px-2 font-mono text-muted-foreground text-xs\">\n        <input\n          type=\"checkbox\"\n          aria-label=\"universal — accept any value (*)\"\n          checked={universal}\n          onChange={(e) => toggleUniversal(e.target.checked)}\n          className=\"size-3.5\"\n        />\n        universal (*)\n      </label>\n\n      {universal ? (\n        <p className=\"font-mono text-muted-foreground text-xs\">\n          accepts any value\n        </p>\n      ) : (\n        <>\n          <ul className=\"flex flex-wrap items-center gap-1.5\">\n            {components.map((c, i) => (\n              <li\n                // biome-ignore lint/suspicious/noArrayIndexKey: chips are positional, reordered only by move/remove\n                key={`component-${i}`}\n                aria-label={`component ${i + 1}`}\n                className=\"flex items-center gap-1\"\n              >\n                {i > 0 && (\n                  <span\n                    aria-hidden=\"true\"\n                    className=\"font-mono text-muted-foreground text-xs\"\n                  >\n                    |\n                  </span>\n                )}\n                <div className=\"flex items-center gap-1 rounded-md border border-input p-1\">\n                  <span className=\"px-1 font-mono text-foreground text-xs\">\n                    {c.base}\n                  </span>\n\n                  <div className=\"flex items-center gap-0.5\">\n                    {/* a labelled segmented control — each button carries its\n                    own `Set component N multiplier to …` label + aria-pressed */}\n                    {MULTIPLIERS.map(({ token, label }) => (\n                      <button\n                        key={token || \"none\"}\n                        type=\"button\"\n                        aria-label={`Set component ${i + 1} multiplier to ${\n                          token === \"\" ? \"none\" : token\n                        }`}\n                        aria-pressed={c.multiplier === token}\n                        onClick={() => updateAt(i, { ...c, multiplier: token })}\n                        className={cn(\n                          \"h-5 w-5 rounded font-mono text-xs hover:bg-muted/50\",\n                          c.multiplier === token &&\n                            \"bg-primary text-primary-foreground\",\n                        )}\n                      >\n                        {label}\n                      </button>\n                    ))}\n                  </div>\n\n                  <button\n                    type=\"button\"\n                    aria-label={`Move component ${i + 1} left`}\n                    onClick={() => move(i, -1)}\n                    disabled={i === 0}\n                    className=\"h-5 w-5 rounded font-mono text-xs hover:bg-muted/50 disabled:opacity-40\"\n                  >\n                    ←\n                  </button>\n                  <button\n                    type=\"button\"\n                    aria-label={`Move component ${i + 1} right`}\n                    onClick={() => move(i, 1)}\n                    disabled={i === components.length - 1}\n                    className=\"h-5 w-5 rounded font-mono text-xs hover:bg-muted/50 disabled:opacity-40\"\n                  >\n                    →\n                  </button>\n                  <button\n                    type=\"button\"\n                    aria-label={`Remove component ${i + 1}`}\n                    onClick={() => removeAt(i)}\n                    className=\"h-5 w-5 rounded font-mono text-xs hover:bg-muted/50\"\n                  >\n                    ×\n                  </button>\n                </div>\n              </li>\n            ))}\n          </ul>\n\n          <div className=\"flex flex-wrap gap-1\">\n            {dataTypeNames().map((name) => (\n              <button\n                key={name}\n                type=\"button\"\n                aria-label={`Add <${name}>`}\n                onClick={() => addType(name)}\n                className=\"h-7 rounded-md border border-input bg-background px-2 font-mono text-muted-foreground text-xs hover:text-foreground\"\n              >\n                {`<${name}>`}\n              </button>\n            ))}\n          </div>\n\n          <div className=\"flex items-center gap-1.5\">\n            <input\n              aria-label=\"literal ident to add\"\n              value={ident}\n              spellCheck={false}\n              autoComplete=\"off\"\n              placeholder=\"literal ident (e.g. auto)\"\n              onChange={(e) => setIdent(e.target.value)}\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\") {\n                  e.preventDefault()\n                  addIdent()\n                }\n              }}\n              className=\"h-8 w-[180px] rounded-md border border-input bg-background px-2 font-mono text-xs\"\n            />\n            <button\n              type=\"button\"\n              aria-label=\"Add the literal ident\"\n              onClick={addIdent}\n              className=\"h-8 rounded-md border border-dashed bg-background px-2 font-mono text-muted-foreground text-xs hover:text-foreground\"\n            >\n              + add ident\n            </button>\n          </div>\n        </>\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/syntax-chip-builder.tsx"
    },
    {
      "path": "src/components/ui/property-syntax-editor/initial-value-field.tsx",
      "content": "\"use client\"\n\nimport { Input } from \"@/components/ui/input\"\nimport { cn } from \"@/lib/utils\"\nimport { matchesSyntax } from \"./property-syntax-editor.helpers\"\n\n// ---------------------------------------------------------------------------\n// InitialValueField (public) — the live demo of the dependent type. A demo\n// `<input>` for a candidate `initial-value`, a green/red status badge driven by\n// the runtime `matchesSyntax(syntax, value)` (the runtime mirror of the\n// `InitialValueLiteral<Syntax, V>` type), and the `inherits` descriptor toggle.\n// The badge carries `role=\"status\"` so assistive tech announces match changes.\n// `<color>` defers to color-picker's `isColorString` — so `#ff0000` is valid\n// but the named color `red` is (correctly) rejected.\n// ---------------------------------------------------------------------------\n\nexport interface InitialValueFieldProps {\n  syntax: string\n  value: string\n  inherits: boolean\n  onValueChange: (value: string) => void\n  onInheritsChange: (inherits: boolean) => void\n  className?: string\n}\n\nexport function InitialValueField({\n  syntax,\n  value,\n  inherits,\n  onValueChange,\n  onInheritsChange,\n  className,\n}: InitialValueFieldProps) {\n  const valid = matchesSyntax(syntax, value)\n\n  return (\n    <div className={cn(\"space-y-2\", className)}>\n      <div className=\"flex flex-wrap items-center gap-2\">\n        <Input\n          aria-label=\"initial value (demo)\"\n          value={value}\n          spellCheck={false}\n          autoComplete=\"off\"\n          placeholder=\"initial-value\"\n          onChange={(e) => onValueChange(e.target.value)}\n          className=\"h-8 w-[200px] font-mono text-xs\"\n        />\n        <span\n          role=\"status\"\n          aria-label=\"initial value status\"\n          className={cn(\n            \"rounded-md px-2 py-1 font-mono text-xs\",\n            valid\n              ? \"bg-emerald-500/15 text-emerald-600 dark:text-emerald-400\"\n              : \"bg-red-500/15 text-red-600 dark:text-red-400\",\n          )}\n        >\n          {valid ? \"valid\" : \"invalid\"}\n        </span>\n      </div>\n\n      <label className=\"flex h-8 w-fit items-center gap-1.5 rounded-md border border-input px-2 font-mono text-muted-foreground text-xs\">\n        <input\n          type=\"checkbox\"\n          aria-label=\"inherits\"\n          checked={inherits}\n          onChange={(e) => onInheritsChange(e.target.checked)}\n          className=\"size-3.5\"\n        />\n        inherits\n      </label>\n    </div>\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/initial-value-field.tsx"
    },
    {
      "path": "src/components/ui/property-syntax-editor/mini-select.tsx",
      "content": "\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport { cn } from \"@/lib/utils\"\n\n// ---------------------------------------------------------------------------\n// MiniSelect — the compact `<select>` chrome shared by every property-syntax-\n// editor dropdown. Owns the one class-string so the controls never drift. A\n// LOCAL copy: registry self-containment means this component carries its own\n// MiniSelect rather than importing another component's (`shadcn add` must pull\n// a self-contained tree). `onValueChange` hands back the raw `e.target.value`.\n// ---------------------------------------------------------------------------\n\nexport const selectClass =\n  \"h-8 rounded-md border border-input bg-background px-1 font-mono text-xs\"\n\nexport interface MiniSelectProps {\n  \"aria-label\": string\n  value: string\n  onValueChange: (value: string) => void\n  children: ReactNode\n  className?: string\n}\n\nexport function MiniSelect({\n  \"aria-label\": ariaLabel,\n  value,\n  onValueChange,\n  children,\n  className,\n}: MiniSelectProps) {\n  return (\n    <select\n      aria-label={ariaLabel}\n      value={value}\n      onChange={(e) => onValueChange(e.target.value)}\n      className={cn(selectClass, className)}\n    >\n      {children}\n    </select>\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/property-syntax-editor/mini-select.tsx"
    }
  ],
  "type": "registry:ui"
}