@flanksource/clicky-ui 0.2.21 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/components/Combobox.cjs +5 -2
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts.map +1 -1
  4. package/dist/components/Combobox.js +5 -2
  5. package/dist/components/Combobox.js.map +1 -1
  6. package/dist/components/FilterBar.cjs +280 -253
  7. package/dist/components/FilterBar.cjs.map +1 -1
  8. package/dist/components/FilterBar.d.ts.map +1 -1
  9. package/dist/components/FilterBar.js +280 -253
  10. package/dist/components/FilterBar.js.map +1 -1
  11. package/dist/components/JsonSchemaForm.cjs +3 -1
  12. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  13. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  14. package/dist/components/JsonSchemaForm.js +4 -2
  15. package/dist/components/JsonSchemaForm.js.map +1 -1
  16. package/dist/components/MultiSelect.cjs +29 -32
  17. package/dist/components/MultiSelect.cjs.map +1 -1
  18. package/dist/components/MultiSelect.d.ts.map +1 -1
  19. package/dist/components/MultiSelect.js +30 -33
  20. package/dist/components/MultiSelect.js.map +1 -1
  21. package/dist/components/TimeRange.cjs +29 -37
  22. package/dist/components/TimeRange.cjs.map +1 -1
  23. package/dist/components/TimeRange.d.ts.map +1 -1
  24. package/dist/components/TimeRange.js +30 -38
  25. package/dist/components/TimeRange.js.map +1 -1
  26. package/dist/components/TreePickerField.cjs +6 -3
  27. package/dist/components/TreePickerField.cjs.map +1 -1
  28. package/dist/components/TreePickerField.d.ts.map +1 -1
  29. package/dist/components/TreePickerField.js +6 -3
  30. package/dist/components/TreePickerField.js.map +1 -1
  31. package/dist/components/icon-menu-picker.cjs +9 -3
  32. package/dist/components/icon-menu-picker.cjs.map +1 -1
  33. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  34. package/dist/components/icon-menu-picker.js +9 -3
  35. package/dist/components/icon-menu-picker.js.map +1 -1
  36. package/dist/components/json-schema-form-fields.cjs +80 -2
  37. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  38. package/dist/components/json-schema-form-fields.d.ts +15 -0
  39. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  40. package/dist/components/json-schema-form-fields.js +81 -3
  41. package/dist/components/json-schema-form-fields.js.map +1 -1
  42. package/dist/components/json-schema-form-refs.cjs +60 -0
  43. package/dist/components/json-schema-form-refs.cjs.map +1 -0
  44. package/dist/components/json-schema-form-refs.d.ts +3 -0
  45. package/dist/components/json-schema-form-refs.d.ts.map +1 -0
  46. package/dist/components/json-schema-form-refs.js +60 -0
  47. package/dist/components/json-schema-form-refs.js.map +1 -0
  48. package/dist/components/json-schema-form-render.cjs +14 -2
  49. package/dist/components/json-schema-form-render.cjs.map +1 -1
  50. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  51. package/dist/components/json-schema-form-render.js +15 -3
  52. package/dist/components/json-schema-form-render.js.map +1 -1
  53. package/dist/components/json-schema-form-resolve.cjs +5 -0
  54. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  55. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  56. package/dist/components/json-schema-form-resolve.js +5 -0
  57. package/dist/components/json-schema-form-resolve.js.map +1 -1
  58. package/dist/components/json-schema-form-template-menu.cjs +83 -0
  59. package/dist/components/json-schema-form-template-menu.cjs.map +1 -0
  60. package/dist/components/json-schema-form-template-menu.d.ts +15 -0
  61. package/dist/components/json-schema-form-template-menu.d.ts.map +1 -0
  62. package/dist/components/json-schema-form-template-menu.js +83 -0
  63. package/dist/components/json-schema-form-template-menu.js.map +1 -0
  64. package/dist/components/json-schema-form-template.cjs +18 -0
  65. package/dist/components/json-schema-form-template.cjs.map +1 -0
  66. package/dist/components/json-schema-form-template.d.ts +13 -0
  67. package/dist/components/json-schema-form-template.d.ts.map +1 -0
  68. package/dist/components/json-schema-form-template.js +18 -0
  69. package/dist/components/json-schema-form-template.js.map +1 -0
  70. package/dist/components/json-schema-form-types.d.ts +5 -1
  71. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  72. package/dist/components.cjs +4 -0
  73. package/dist/components.cjs.map +1 -1
  74. package/dist/components.d.ts +3 -0
  75. package/dist/components.d.ts.map +1 -1
  76. package/dist/components.js +4 -0
  77. package/dist/components.js.map +1 -1
  78. package/dist/data/Clicky.cjs +6 -9
  79. package/dist/data/Clicky.cjs.map +1 -1
  80. package/dist/data/Clicky.d.ts.map +1 -1
  81. package/dist/data/Clicky.js +6 -9
  82. package/dist/data/Clicky.js.map +1 -1
  83. package/dist/data/DataTable.cjs +23 -17
  84. package/dist/data/DataTable.cjs.map +1 -1
  85. package/dist/data/DataTable.d.ts +7 -1
  86. package/dist/data/DataTable.d.ts.map +1 -1
  87. package/dist/data/DataTable.js +24 -18
  88. package/dist/data/DataTable.js.map +1 -1
  89. package/dist/data/Gauge.cjs +37 -4
  90. package/dist/data/Gauge.cjs.map +1 -1
  91. package/dist/data/Gauge.d.ts +6 -1
  92. package/dist/data/Gauge.d.ts.map +1 -1
  93. package/dist/data/Gauge.js +37 -4
  94. package/dist/data/Gauge.js.map +1 -1
  95. package/dist/data/TimeseriesCoreBars.cjs +127 -29
  96. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  97. package/dist/data/TimeseriesCoreBars.d.ts +11 -1
  98. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  99. package/dist/data/TimeseriesCoreBars.js +128 -30
  100. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  101. package/dist/data/TimeseriesGauge.cjs +105 -47
  102. package/dist/data/TimeseriesGauge.cjs.map +1 -1
  103. package/dist/data/TimeseriesGauge.d.ts +6 -1
  104. package/dist/data/TimeseriesGauge.d.ts.map +1 -1
  105. package/dist/data/TimeseriesGauge.js +105 -47
  106. package/dist/data/TimeseriesGauge.js.map +1 -1
  107. package/dist/data/WorkloadCard.cjs +371 -0
  108. package/dist/data/WorkloadCard.cjs.map +1 -0
  109. package/dist/data/WorkloadCard.d.ts +24 -0
  110. package/dist/data/WorkloadCard.d.ts.map +1 -0
  111. package/dist/data/WorkloadCard.js +371 -0
  112. package/dist/data/WorkloadCard.js.map +1 -0
  113. package/dist/data/WorkloadCard.model.cjs +51 -0
  114. package/dist/data/WorkloadCard.model.cjs.map +1 -0
  115. package/dist/data/WorkloadCard.model.d.ts +53 -0
  116. package/dist/data/WorkloadCard.model.d.ts.map +1 -0
  117. package/dist/data/WorkloadCard.model.js +51 -0
  118. package/dist/data/WorkloadCard.model.js.map +1 -0
  119. package/dist/data/ai/ChatFab.cjs +3 -1
  120. package/dist/data/ai/ChatFab.cjs.map +1 -1
  121. package/dist/data/ai/ChatFab.d.ts.map +1 -1
  122. package/dist/data/ai/ChatFab.js +3 -1
  123. package/dist/data/ai/ChatFab.js.map +1 -1
  124. package/dist/data/ai/ChatWindow.cjs +25 -7
  125. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  126. package/dist/data/ai/ChatWindow.d.ts +11 -3
  127. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  128. package/dist/data/ai/ChatWindow.js +26 -8
  129. package/dist/data/ai/ChatWindow.js.map +1 -1
  130. package/dist/data/ai/chat-window-context.cjs +2 -1
  131. package/dist/data/ai/chat-window-context.cjs.map +1 -1
  132. package/dist/data/ai/chat-window-context.d.ts +1 -1
  133. package/dist/data/ai/chat-window-context.d.ts.map +1 -1
  134. package/dist/data/ai/chat-window-context.js +2 -1
  135. package/dist/data/ai/chat-window-context.js.map +1 -1
  136. package/dist/data/ai/context.cjs.map +1 -1
  137. package/dist/data/ai/context.d.ts +3 -0
  138. package/dist/data/ai/context.d.ts.map +1 -1
  139. package/dist/data/ai/context.js.map +1 -1
  140. package/dist/data/chat/Chat.cjs +9 -1
  141. package/dist/data/chat/Chat.cjs.map +1 -1
  142. package/dist/data/chat/Chat.d.ts +4 -2
  143. package/dist/data/chat/Chat.d.ts.map +1 -1
  144. package/dist/data/chat/Chat.js +10 -2
  145. package/dist/data/chat/Chat.js.map +1 -1
  146. package/dist/data/chat/Message.cjs +13 -4
  147. package/dist/data/chat/Message.cjs.map +1 -1
  148. package/dist/data/chat/Message.d.ts +4 -2
  149. package/dist/data/chat/Message.d.ts.map +1 -1
  150. package/dist/data/chat/Message.js +13 -4
  151. package/dist/data/chat/Message.js.map +1 -1
  152. package/dist/data/chat/ToolCall.cjs +9 -4
  153. package/dist/data/chat/ToolCall.cjs.map +1 -1
  154. package/dist/data/chat/ToolCall.d.ts +3 -2
  155. package/dist/data/chat/ToolCall.d.ts.map +1 -1
  156. package/dist/data/chat/ToolCall.js +9 -4
  157. package/dist/data/chat/ToolCall.js.map +1 -1
  158. package/dist/data/chat/index.d.ts +1 -1
  159. package/dist/data/chat/index.d.ts.map +1 -1
  160. package/dist/data/chat/types.cjs.map +1 -1
  161. package/dist/data/chat/types.d.ts +7 -0
  162. package/dist/data/chat/types.d.ts.map +1 -1
  163. package/dist/data/chat/types.js.map +1 -1
  164. package/dist/data/version-info.cjs +2 -2
  165. package/dist/data/version-info.js +2 -2
  166. package/dist/data.cjs +7 -0
  167. package/dist/data.cjs.map +1 -1
  168. package/dist/data.d.ts +5 -3
  169. package/dist/data.d.ts.map +1 -1
  170. package/dist/data.js +8 -1
  171. package/dist/data.js.map +1 -1
  172. package/dist/hooks/use-sort.cjs +19 -10
  173. package/dist/hooks/use-sort.cjs.map +1 -1
  174. package/dist/hooks/use-sort.d.ts +3 -0
  175. package/dist/hooks/use-sort.d.ts.map +1 -1
  176. package/dist/hooks/use-sort.js +19 -10
  177. package/dist/hooks/use-sort.js.map +1 -1
  178. package/dist/index.cjs +11 -0
  179. package/dist/index.cjs.map +1 -1
  180. package/dist/index.js +11 -0
  181. package/dist/index.js.map +1 -1
  182. package/dist/layout/AppShell.cjs +6 -10
  183. package/dist/layout/AppShell.cjs.map +1 -1
  184. package/dist/layout/AppShell.d.ts +2 -0
  185. package/dist/layout/AppShell.d.ts.map +1 -1
  186. package/dist/layout/AppShell.js +6 -10
  187. package/dist/layout/AppShell.js.map +1 -1
  188. package/dist/overlay/DropdownMenu.cjs +50 -38
  189. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  190. package/dist/overlay/DropdownMenu.d.ts +5 -1
  191. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  192. package/dist/overlay/DropdownMenu.js +50 -38
  193. package/dist/overlay/DropdownMenu.js.map +1 -1
  194. package/dist/overlay/HoverCard.cjs +7 -2
  195. package/dist/overlay/HoverCard.cjs.map +1 -1
  196. package/dist/overlay/HoverCard.d.ts.map +1 -1
  197. package/dist/overlay/HoverCard.js +7 -2
  198. package/dist/overlay/HoverCard.js.map +1 -1
  199. package/dist/overlay/Modal.cjs +20 -17
  200. package/dist/overlay/Modal.cjs.map +1 -1
  201. package/dist/overlay/Modal.d.ts.map +1 -1
  202. package/dist/overlay/Modal.js +21 -18
  203. package/dist/overlay/Modal.js.map +1 -1
  204. package/dist/overlay/Toast.cjs +9 -1
  205. package/dist/overlay/Toast.cjs.map +1 -1
  206. package/dist/overlay/Toast.d.ts.map +1 -1
  207. package/dist/overlay/Toast.js +9 -1
  208. package/dist/overlay/Toast.js.map +1 -1
  209. package/dist/overlay/modalStack.cjs +53 -1
  210. package/dist/overlay/modalStack.cjs.map +1 -1
  211. package/dist/overlay/modalStack.d.ts +12 -0
  212. package/dist/overlay/modalStack.d.ts.map +1 -1
  213. package/dist/overlay/modalStack.js +54 -2
  214. package/dist/overlay/modalStack.js.map +1 -1
  215. package/dist/overlay/zIndex.cjs +20 -0
  216. package/dist/overlay/zIndex.cjs.map +1 -0
  217. package/dist/overlay/zIndex.d.ts +39 -0
  218. package/dist/overlay/zIndex.d.ts.map +1 -0
  219. package/dist/overlay/zIndex.js +20 -0
  220. package/dist/overlay/zIndex.js.map +1 -0
  221. package/dist/rpc/AcceptPicker.cjs +28 -26
  222. package/dist/rpc/AcceptPicker.cjs.map +1 -1
  223. package/dist/rpc/AcceptPicker.d.ts.map +1 -1
  224. package/dist/rpc/AcceptPicker.js +29 -27
  225. package/dist/rpc/AcceptPicker.js.map +1 -1
  226. package/dist/styles.css +1 -1
  227. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"icon-menu-picker.js","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n useClick,\n useDismiss,\n useFloating,\n useInteractions,\n useListNavigation,\n useMergeRefs,\n useRole,\n useTypeahead,\n} from \"@floating-ui/react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./IconButton\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [open, setOpen] = useState(false);\n // Index of the item highlighted by keyboard navigation. Null until the user\n // arrows/typeaheads, so a pointer-opened menu starts with the selected row\n // focused (selectedIndex below) rather than the first.\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const listRef = useRef<(HTMLElement | null)[]>([]);\n const labelsRef = useRef<string[]>(options.map((o) => o.label));\n labelsRef.current = options.map((o) => o.label);\n\n const selectedIndex = useMemo(\n () => options.findIndex((option) => option.value === value),\n [options, value],\n );\n\n const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({\n open,\n onOpenChange: setOpen,\n placement: \"bottom-start\",\n whileElementsMounted: autoUpdate,\n // offset(6) reproduces the previous 0.375rem gap; flip opens the menu\n // upward when the trigger is near the bottom edge; shift slides it back\n // on-screen at the left/right edges. The 8px padding keeps a gutter.\n middleware: [offset(6), flip({ padding: 8 }), shift({ padding: 8 })],\n });\n\n const click = useClick(context);\n const dismiss = useDismiss(context);\n const role = useRole(context, { role: \"menu\" });\n const listNav = useListNavigation(context, {\n listRef,\n activeIndex,\n selectedIndex,\n onNavigate: setActiveIndex,\n loop: true,\n });\n const typeahead = useTypeahead(context, {\n listRef: labelsRef,\n activeIndex,\n onMatch: setActiveIndex,\n });\n\n const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([\n click,\n dismiss,\n role,\n listNav,\n typeahead,\n ]);\n\n // Keep the trigger wrapper available both to floating-ui (as the positioning\n // reference) and to the consumer's forwarded ref.\n const referenceRef = useMergeRefs([refs.setReference, forwardedRef]);\n\n const select = (next: T) => {\n onChange(next);\n setOpen(false);\n };\n\n const selected = options[selectedIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n const triggerTitleText = triggerTitle ?? `${ariaLabel}: ${selected.label}`;\n\n return (\n <div ref={referenceRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n {showLabel ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"default\"\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitleText}\n className={cn(\n \"w-full justify-start text-muted-foreground hover:text-foreground\",\n triggerClassName,\n )}\n {...getReferenceProps()}\n >\n <Icon icon={selected.icon} className=\"shrink-0 text-foreground\" />\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </Button>\n ) : (\n <IconButton\n icon={selected.icon}\n label={ariaLabel}\n title={triggerTitleText}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={triggerClassName}\n {...getReferenceProps()}\n />\n )}\n {open && (\n <FloatingPortal>\n <FloatingFocusManager context={context} modal={false} initialFocus={selectedIndex}>\n <div\n ref={refs.setFloating}\n role=\"menu\"\n aria-label={ariaLabel}\n style={floatingStyles}\n className={cn(\n \"z-[9999] min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n {...getFloatingProps()}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n listRef.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={index === activeIndex ? 0 : -1}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n {...getItemProps({\n onClick: () => select(option.value),\n onKeyDown: (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n },\n })}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n </FloatingFocusManager>\n </FloatingPortal>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"icon-menu-picker.js","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n useClick,\n useDismiss,\n useFloating,\n useInteractions,\n useListNavigation,\n useMergeRefs,\n useRole,\n useTypeahead,\n} from \"@floating-ui/react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { useEscapeLayer, useFloatingZIndex } from \"../overlay/modalStack\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./IconButton\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [open, setOpen] = useState(false);\n const floatingZ = useFloatingZIndex();\n // Index of the item highlighted by keyboard navigation. Null until the user\n // arrows/typeaheads, so a pointer-opened menu starts with the selected row\n // focused (selectedIndex below) rather than the first.\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const listRef = useRef<(HTMLElement | null)[]>([]);\n const labelsRef = useRef<string[]>(options.map((o) => o.label));\n labelsRef.current = options.map((o) => o.label);\n\n const selectedIndex = useMemo(\n () => options.findIndex((option) => option.value === value),\n [options, value],\n );\n\n const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({\n open,\n onOpenChange: setOpen,\n placement: \"bottom-start\",\n whileElementsMounted: autoUpdate,\n // offset(6) reproduces the previous 0.375rem gap; flip opens the menu\n // upward when the trigger is near the bottom edge; shift slides it back\n // on-screen at the left/right edges. The 8px padding keeps a gutter.\n middleware: [offset(6), flip({ padding: 8 }), shift({ padding: 8 })],\n });\n\n const click = useClick(context);\n const dismiss = useDismiss(context, { escapeKey: false });\n const role = useRole(context, { role: \"menu\" });\n const listNav = useListNavigation(context, {\n listRef,\n activeIndex,\n selectedIndex,\n onNavigate: setActiveIndex,\n loop: true,\n });\n const typeahead = useTypeahead(context, {\n listRef: labelsRef,\n activeIndex,\n onMatch: setActiveIndex,\n });\n\n const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([\n click,\n dismiss,\n role,\n listNav,\n typeahead,\n ]);\n useEscapeLayer(open, () => {\n setOpen(false);\n if (refs.domReference.current instanceof HTMLElement) refs.domReference.current.focus();\n });\n\n // Keep the trigger wrapper available both to floating-ui (as the positioning\n // reference) and to the consumer's forwarded ref.\n const referenceRef = useMergeRefs([refs.setReference, forwardedRef]);\n\n const select = (next: T) => {\n onChange(next);\n setOpen(false);\n };\n\n const selected = options[selectedIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n const triggerTitleText = triggerTitle ?? `${ariaLabel}: ${selected.label}`;\n\n return (\n <div ref={referenceRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n {showLabel ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"default\"\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitleText}\n className={cn(\n \"w-full justify-start text-muted-foreground hover:text-foreground\",\n triggerClassName,\n )}\n {...getReferenceProps()}\n >\n <Icon icon={selected.icon} className=\"shrink-0 text-foreground\" />\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </Button>\n ) : (\n <IconButton\n icon={selected.icon}\n label={ariaLabel}\n title={triggerTitleText}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={triggerClassName}\n {...getReferenceProps()}\n />\n )}\n {open && (\n <FloatingPortal>\n <FloatingFocusManager context={context} modal={false} initialFocus={selectedIndex}>\n <div\n ref={refs.setFloating}\n role=\"menu\"\n aria-label={ariaLabel}\n style={{ ...floatingStyles, zIndex: floatingZ }}\n className={cn(\n \"min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n {...getFloatingProps()}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n listRef.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={index === activeIndex ? 0 : -1}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n {...getItemProps({\n onClick: () => select(option.value),\n onKeyDown: (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n },\n })}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n </FloatingFocusManager>\n </FloatingPortal>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":[],"mappings":"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"}
@@ -136,14 +136,15 @@ function NumberControl({
136
136
  size
137
137
  }) {
138
138
  const coerce = field.coerceNumber !== false;
139
- return /* @__PURE__ */ jsxRuntime.jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
139
+ const suffix = field.suffix ?? (field.unit ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none select-none text-muted-foreground", children: field.unit }) : void 0);
140
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
140
141
  "input",
141
142
  {
142
143
  id: fieldId,
143
144
  type: "text",
144
145
  inputMode: "decimal",
145
146
  "data-jsf-input": true,
146
- className: utils.cn(jsonSchemaFormUtils.inputClass(size), field.prefix && "pl-8", field.suffix && "pr-8"),
147
+ className: utils.cn(jsonSchemaFormUtils.inputClass(size), field.prefix && "pl-8", suffix && "pr-8"),
147
148
  value: jsonSchemaFormUtils.toText(field.value),
148
149
  disabled: readOnly,
149
150
  placeholder: jsonSchemaFormUtils.defaultPlaceholder(field.schema),
@@ -295,14 +296,91 @@ function RadioGroupControl({
295
296
  }
296
297
  );
297
298
  }
299
+ function TextareaControl({
300
+ field,
301
+ fieldId,
302
+ readOnly,
303
+ size
304
+ }) {
305
+ const text = jsonSchemaFormUtils.toText(field.value);
306
+ if (readOnly) {
307
+ return /* @__PURE__ */ jsxRuntime.jsx(
308
+ "span",
309
+ {
310
+ id: fieldId,
311
+ "data-jsf-input": true,
312
+ "data-jsf-readonly": true,
313
+ className: utils.cn("block whitespace-pre-wrap break-words text-foreground", jsonSchemaFormSize.labelSizeClass[size]),
314
+ title: text || void 0,
315
+ children: text || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "—" })
316
+ }
317
+ );
318
+ }
319
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsxRuntime.jsx(
320
+ "textarea",
321
+ {
322
+ id: fieldId,
323
+ "data-jsf-input": true,
324
+ rows: 4,
325
+ className: utils.cn(jsonSchemaFormUtils.inputClass(size), "h-auto min-h-[5rem] resize-y", field.prefix && "pl-8", field.suffix && "pr-8"),
326
+ value: text,
327
+ disabled: readOnly,
328
+ placeholder: jsonSchemaFormUtils.defaultPlaceholder(field.schema),
329
+ onChange: (e) => field.onChange(e.target.value)
330
+ }
331
+ ) });
332
+ }
333
+ function DisplayControl({ field, size }) {
334
+ const variant = field.displayVariant ?? "text";
335
+ if (variant === "divider") {
336
+ return /* @__PURE__ */ jsxRuntime.jsx("hr", { "data-jsf-input": true, className: "my-1 w-full border-t border-border" });
337
+ }
338
+ if (variant === "spacer") {
339
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-jsf-input": true, "aria-hidden": true, className: "h-2" });
340
+ }
341
+ if (variant === "heading") {
342
+ return /* @__PURE__ */ jsxRuntime.jsx(
343
+ "div",
344
+ {
345
+ "data-jsf-input": true,
346
+ className: utils.cn("border-b border-border pb-1 font-semibold text-foreground", jsonSchemaFormSize.labelSizeClass[size]),
347
+ children: field.label
348
+ }
349
+ );
350
+ }
351
+ const text = field.description ?? jsonSchemaFormUtils.toText(field.value) ?? field.label;
352
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { "data-jsf-input": true, className: utils.cn("text-muted-foreground", jsonSchemaFormSize.labelSizeClass[size]), children: text || field.label });
353
+ }
354
+ function LinkControl({ field, fieldId, size }) {
355
+ const text = jsonSchemaFormUtils.toText(field.value);
356
+ const href = field.href ?? (/^https?:\/\//i.test(text) ? text : void 0);
357
+ if (!href) {
358
+ return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyValue, { field, fieldId, size });
359
+ }
360
+ return /* @__PURE__ */ jsxRuntime.jsx(
361
+ "a",
362
+ {
363
+ id: fieldId,
364
+ "data-jsf-input": true,
365
+ href,
366
+ target: "_blank",
367
+ rel: "noreferrer noopener",
368
+ className: utils.cn("inline-flex items-center text-primary underline underline-offset-2", jsonSchemaFormSize.controlHeightClass[size], jsonSchemaFormSize.labelSizeClass[size]),
369
+ children: text || field.label || href
370
+ }
371
+ );
372
+ }
298
373
  exports.BooleanControl = BooleanControl;
299
374
  exports.DateControl = DateControl;
375
+ exports.DisplayControl = DisplayControl;
300
376
  exports.EnumControl = EnumControl;
301
377
  exports.FieldLabel = FieldLabel;
302
378
  exports.FieldWrapper = FieldWrapper;
303
379
  exports.FieldsGrid = FieldsGrid;
380
+ exports.LinkControl = LinkControl;
304
381
  exports.NumberControl = NumberControl;
305
382
  exports.ObjectSection = ObjectSection;
306
383
  exports.ReadOnlyValue = ReadOnlyValue;
307
384
  exports.StringControl = StringControl;
385
+ exports.TextareaControl = TextareaControl;
308
386
  //# sourceMappingURL=json-schema-form-fields.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-fields.cjs","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldAdornmentWrapper positions a field's leading/trailing in-field adornments\n// (FieldControl.prefix at the left edge, FieldControl.suffix at the right). The\n// container is tagged data-jsf-control so an adornment can locate its sibling\n// input[data-jsf-input] (e.g. for caret-aware insertion). With neither adornment\n// it renders the input bare, so unadorned fields are byte-for-byte unchanged.\nfunction FieldAdornmentWrapper({\n prefix,\n suffix,\n children,\n}: {\n prefix?: ReactNode;\n suffix?: ReactNode;\n children: ReactNode;\n}) {\n if (!prefix && !suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {prefix && <div className=\"absolute inset-y-0 left-1.5 flex items-center\">{prefix}</div>}\n {children}\n {suffix && <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>}\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n const coerce = field.coerceNumber !== false;\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n // Coerce mid-typing ONLY when the parse round-trips to the same text\n // (e.g. \"100\", \"33.5\") — so an in-progress decimal like \"33.\" or a\n // trailing-zero \"33.30\" keeps its raw text instead of collapsing to a\n // number and stranding the caret. Non-numeric tokens fall through to\n // the raw string. Blur finalizes any still-uncoerced numeric text.\n if (coerce && raw.trim() !== \"\" && String(Number(raw)) === raw.trim()) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n onBlur={(e) => {\n const raw = e.target.value;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n }\n }}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n prefix={field.prefix}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n prefix={field.prefix}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":["jsx","cn","inlineRowGapClass","stackedRowGapClass","jsxs","controlMinHeightClass","labelSizeClass","LabelIcon","fieldInnerGapClass","toText","formatDateTimeRelative","controlHeightClass","inputClass","defaultPlaceholder","DateTimePicker","withSyntheticValue","Combobox"],"mappings":"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"}
1
+ {"version":3,"file":"json-schema-form-fields.cjs","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldAdornmentWrapper positions a field's leading/trailing in-field adornments\n// (FieldControl.prefix at the left edge, FieldControl.suffix at the right). The\n// container is tagged data-jsf-control so an adornment can locate its sibling\n// input[data-jsf-input] (e.g. for caret-aware insertion). With neither adornment\n// it renders the input bare, so unadorned fields are byte-for-byte unchanged.\nfunction FieldAdornmentWrapper({\n prefix,\n suffix,\n children,\n}: {\n prefix?: ReactNode;\n suffix?: ReactNode;\n children: ReactNode;\n}) {\n if (!prefix && !suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {prefix && <div className=\"absolute inset-y-0 left-1.5 flex items-center\">{prefix}</div>}\n {children}\n {suffix && <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>}\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n const coerce = field.coerceNumber !== false;\n // A static unit (e.g. \"%\") rides the right edge of the input as a display-only\n // suffix; a consumer-set `suffix` (e.g. an insert-snippet button) wins if both\n // are present (number insert triggers mount on the left as `prefix`).\n const suffix = field.suffix ?? (field.unit ? (\n <span className=\"pointer-events-none select-none text-muted-foreground\">{field.unit}</span>\n ) : undefined);\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n // Coerce mid-typing ONLY when the parse round-trips to the same text\n // (e.g. \"100\", \"33.5\") — so an in-progress decimal like \"33.\" or a\n // trailing-zero \"33.30\" keeps its raw text instead of collapsing to a\n // number and stranding the caret. Non-numeric tokens fall through to\n // the raw string. Blur finalizes any still-uncoerced numeric text.\n if (coerce && raw.trim() !== \"\" && String(Number(raw)) === raw.trim()) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n onBlur={(e) => {\n const raw = e.target.value;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n }\n }}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n prefix={field.prefix}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n prefix={field.prefix}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// TextareaControl edits a long-form string in a multi-line box. Read-only, it\n// shows the value with line breaks preserved; editable, a resizable <textarea>.\n// The raw string is committed unchanged so a consumer-permitted template token\n// survives.\nexport function TextareaControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"block whitespace-pre-wrap break-words text-foreground\", labelSizeClass[size])}\n title={text || undefined}\n >\n {text || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n }\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <textarea\n id={fieldId}\n data-jsf-input\n rows={4}\n className={cn(inputClass(size), \"h-auto min-h-[5rem] resize-y\", field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={text}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DisplayControl renders a static, non-editable presentation element:\n// Label/Title (heading), Message (info text), Line (divider), or Blank/Filler\n// (spacer). It never collects a value; the heading/text source is the field's\n// title (label) and, for \"text\", its description. Rendered identically in the\n// form and the read-only Viewer.\nexport function DisplayControl({ field, size }: { field: FieldControl; size: FormSize }) {\n const variant = field.displayVariant ?? \"text\";\n if (variant === \"divider\") {\n return <hr data-jsf-input className=\"my-1 w-full border-t border-border\" />;\n }\n if (variant === \"spacer\") {\n return <div data-jsf-input aria-hidden className=\"h-2\" />;\n }\n if (variant === \"heading\") {\n return (\n <div\n data-jsf-input\n className={cn(\"border-b border-border pb-1 font-semibold text-foreground\", labelSizeClass[size])}\n >\n {field.label}\n </div>\n );\n }\n const text = field.description ?? toText(field.value) ?? field.label;\n return (\n <p data-jsf-input className={cn(\"text-muted-foreground\", labelSizeClass[size])}>\n {text || field.label}\n </p>\n );\n}\n\n// LinkControl renders a read-only external hyperlink. The\n// href is field.href, falling back to the value when it is an absolute URL; with\n// neither it degrades to a plain read-only value.\nexport function LinkControl({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const href = field.href ?? (/^https?:\\/\\//i.test(text) ? text : undefined);\n if (!href) {\n return <ReadOnlyValue field={field} fieldId={fieldId} size={size} />;\n }\n return (\n <a\n id={fieldId}\n data-jsf-input\n href={href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className={cn(\"inline-flex items-center text-primary underline underline-offset-2\", controlHeightClass[size], labelSizeClass[size])}\n >\n {text || field.label || href}\n </a>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":["jsx","cn","inlineRowGapClass","stackedRowGapClass","jsxs","controlMinHeightClass","labelSizeClass","LabelIcon","fieldInnerGapClass","toText","formatDateTimeRelative","controlHeightClass","inputClass","defaultPlaceholder","DateTimePicker","withSyntheticValue","Combobox"],"mappings":"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"}
@@ -64,4 +64,19 @@ export declare function EnumControl({ field, fieldId, readOnly, size, }: {
64
64
  readOnly: boolean;
65
65
  size: FormSize;
66
66
  }): import("react/jsx-runtime").JSX.Element;
67
+ export declare function TextareaControl({ field, fieldId, readOnly, size, }: {
68
+ field: FieldControl;
69
+ fieldId: string;
70
+ readOnly: boolean;
71
+ size: FormSize;
72
+ }): import("react/jsx-runtime").JSX.Element;
73
+ export declare function DisplayControl({ field, size }: {
74
+ field: FieldControl;
75
+ size: FormSize;
76
+ }): import("react/jsx-runtime").JSX.Element;
77
+ export declare function LinkControl({ field, fieldId, size }: {
78
+ field: FieldControl;
79
+ fieldId: string;
80
+ size: FormSize;
81
+ }): import("react/jsx-runtime").JSX.Element;
67
82
  //# sourceMappingURL=json-schema-form-fields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-fields.d.ts","sourceRoot":"","sources":["../../src/components/json-schema-form-fields.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-schema-form-fields.d.ts","sourceRoot":"","sources":["../../src/components/json-schema-form-fields.tsx"],"names":[],"mappings":"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"}
@@ -134,14 +134,15 @@ function NumberControl({
134
134
  size
135
135
  }) {
136
136
  const coerce = field.coerceNumber !== false;
137
- return /* @__PURE__ */ jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsx(
137
+ const suffix = field.suffix ?? (field.unit ? /* @__PURE__ */ jsx("span", { className: "pointer-events-none select-none text-muted-foreground", children: field.unit }) : void 0);
138
+ return /* @__PURE__ */ jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix, children: /* @__PURE__ */ jsx(
138
139
  "input",
139
140
  {
140
141
  id: fieldId,
141
142
  type: "text",
142
143
  inputMode: "decimal",
143
144
  "data-jsf-input": true,
144
- className: cn(inputClass(size), field.prefix && "pl-8", field.suffix && "pr-8"),
145
+ className: cn(inputClass(size), field.prefix && "pl-8", suffix && "pr-8"),
145
146
  value: toText(field.value),
146
147
  disabled: readOnly,
147
148
  placeholder: defaultPlaceholder(field.schema),
@@ -293,16 +294,93 @@ function RadioGroupControl({
293
294
  }
294
295
  );
295
296
  }
297
+ function TextareaControl({
298
+ field,
299
+ fieldId,
300
+ readOnly,
301
+ size
302
+ }) {
303
+ const text = toText(field.value);
304
+ if (readOnly) {
305
+ return /* @__PURE__ */ jsx(
306
+ "span",
307
+ {
308
+ id: fieldId,
309
+ "data-jsf-input": true,
310
+ "data-jsf-readonly": true,
311
+ className: cn("block whitespace-pre-wrap break-words text-foreground", labelSizeClass[size]),
312
+ title: text || void 0,
313
+ children: text || /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "—" })
314
+ }
315
+ );
316
+ }
317
+ return /* @__PURE__ */ jsx(FieldAdornmentWrapper, { prefix: field.prefix, suffix: field.suffix, children: /* @__PURE__ */ jsx(
318
+ "textarea",
319
+ {
320
+ id: fieldId,
321
+ "data-jsf-input": true,
322
+ rows: 4,
323
+ className: cn(inputClass(size), "h-auto min-h-[5rem] resize-y", field.prefix && "pl-8", field.suffix && "pr-8"),
324
+ value: text,
325
+ disabled: readOnly,
326
+ placeholder: defaultPlaceholder(field.schema),
327
+ onChange: (e) => field.onChange(e.target.value)
328
+ }
329
+ ) });
330
+ }
331
+ function DisplayControl({ field, size }) {
332
+ const variant = field.displayVariant ?? "text";
333
+ if (variant === "divider") {
334
+ return /* @__PURE__ */ jsx("hr", { "data-jsf-input": true, className: "my-1 w-full border-t border-border" });
335
+ }
336
+ if (variant === "spacer") {
337
+ return /* @__PURE__ */ jsx("div", { "data-jsf-input": true, "aria-hidden": true, className: "h-2" });
338
+ }
339
+ if (variant === "heading") {
340
+ return /* @__PURE__ */ jsx(
341
+ "div",
342
+ {
343
+ "data-jsf-input": true,
344
+ className: cn("border-b border-border pb-1 font-semibold text-foreground", labelSizeClass[size]),
345
+ children: field.label
346
+ }
347
+ );
348
+ }
349
+ const text = field.description ?? toText(field.value) ?? field.label;
350
+ return /* @__PURE__ */ jsx("p", { "data-jsf-input": true, className: cn("text-muted-foreground", labelSizeClass[size]), children: text || field.label });
351
+ }
352
+ function LinkControl({ field, fieldId, size }) {
353
+ const text = toText(field.value);
354
+ const href = field.href ?? (/^https?:\/\//i.test(text) ? text : void 0);
355
+ if (!href) {
356
+ return /* @__PURE__ */ jsx(ReadOnlyValue, { field, fieldId, size });
357
+ }
358
+ return /* @__PURE__ */ jsx(
359
+ "a",
360
+ {
361
+ id: fieldId,
362
+ "data-jsf-input": true,
363
+ href,
364
+ target: "_blank",
365
+ rel: "noreferrer noopener",
366
+ className: cn("inline-flex items-center text-primary underline underline-offset-2", controlHeightClass[size], labelSizeClass[size]),
367
+ children: text || field.label || href
368
+ }
369
+ );
370
+ }
296
371
  export {
297
372
  BooleanControl,
298
373
  DateControl,
374
+ DisplayControl,
299
375
  EnumControl,
300
376
  FieldLabel,
301
377
  FieldWrapper,
302
378
  FieldsGrid,
379
+ LinkControl,
303
380
  NumberControl,
304
381
  ObjectSection,
305
382
  ReadOnlyValue,
306
- StringControl
383
+ StringControl,
384
+ TextareaControl
307
385
  };
308
386
  //# sourceMappingURL=json-schema-form-fields.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-form-fields.js","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldAdornmentWrapper positions a field's leading/trailing in-field adornments\n// (FieldControl.prefix at the left edge, FieldControl.suffix at the right). The\n// container is tagged data-jsf-control so an adornment can locate its sibling\n// input[data-jsf-input] (e.g. for caret-aware insertion). With neither adornment\n// it renders the input bare, so unadorned fields are byte-for-byte unchanged.\nfunction FieldAdornmentWrapper({\n prefix,\n suffix,\n children,\n}: {\n prefix?: ReactNode;\n suffix?: ReactNode;\n children: ReactNode;\n}) {\n if (!prefix && !suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {prefix && <div className=\"absolute inset-y-0 left-1.5 flex items-center\">{prefix}</div>}\n {children}\n {suffix && <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>}\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n const coerce = field.coerceNumber !== false;\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n // Coerce mid-typing ONLY when the parse round-trips to the same text\n // (e.g. \"100\", \"33.5\") — so an in-progress decimal like \"33.\" or a\n // trailing-zero \"33.30\" keeps its raw text instead of collapsing to a\n // number and stranding the caret. Non-numeric tokens fall through to\n // the raw string. Blur finalizes any still-uncoerced numeric text.\n if (coerce && raw.trim() !== \"\" && String(Number(raw)) === raw.trim()) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n onBlur={(e) => {\n const raw = e.target.value;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n }\n }}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n prefix={field.prefix}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n prefix={field.prefix}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-schema-form-fields.js","sources":["../../src/components/json-schema-form-fields.tsx"],"sourcesContent":["import { type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { LabelIcon, type LabelIconSpec } from \"../data/Icon\";\nimport { formatDateTimeRelative } from \"../data/cells/timestamp-format\";\nimport { Combobox } from \"./Combobox\";\nimport { DateTimePicker } from \"./DateTimePicker\";\nimport type { FieldControl, FieldOption, FormLayout } from \"./json-schema-form-types\";\nimport {\n defaultPlaceholder,\n inputClass,\n toText,\n withSyntheticValue,\n} from \"./json-schema-form-utils\";\nimport {\n controlHeightClass,\n controlMinHeightClass,\n fieldInnerGapClass,\n inlineRowGapClass,\n labelSizeClass,\n stackedRowGapClass,\n type FormSize,\n} from \"./json-schema-form-size\";\n\n// FieldsGrid is the container every group of field rows renders into — the\n// top-level form and each nested object/map. In inline mode it owns the single\n// 2-column track definition: `fit-content(labelMaxWidth)` for the label column,\n// `minmax(0, valueMaxWidth)` for the value column. Every field row is a\n// `subgrid` that inherits these tracks, so all labels share one column width and\n// every value lines up — a real form grid, not per-row independent grids.\n// (`fit-content()` is the only valid CSS for \"size to content, clamped to a max\"\n// — `min(w, max-content)` is rejected by the browser, which silently drops the\n// whole declaration and collapses every row to a stacked-looking column. The\n// template is an inline style because Tailwind can't interpolate runtime widths.)\n// Stacked mode is a plain single-column grid.\nexport function FieldsGrid({ layout, size, children }: { layout: FormLayout; size: FormSize; children: ReactNode }) {\n if (layout.mode === \"inline\") {\n const labelMaxWidth = layout.labelMaxWidth ?? \"40ch\";\n const valueMaxWidth = layout.valueMaxWidth ?? \"600px\";\n return (\n <div\n className={cn(\"grid\", inlineRowGapClass[size])}\n style={{ gridTemplateColumns: `fit-content(${labelMaxWidth}) minmax(0, ${valueMaxWidth})` }}\n >\n {children}\n </div>\n );\n }\n return <div className={cn(\"grid\", stackedRowGapClass[size])}>{children}</div>;\n}\n\n// FieldWrapper lays out a single label + value (+ helper/error). In inline mode\n// it is a `subgrid` row spanning the parent FieldsGrid's two columns, so its\n// label and value snap to the shared label/value tracks (helper/error sit under\n// the value column). In stacked mode the label sits tight above its control as\n// one unit, capped at valueMaxWidth so controls don't stretch edge to edge.\nexport function FieldWrapper({\n label,\n value,\n helper,\n error,\n layout,\n size,\n}: {\n label: ReactNode;\n value: ReactNode;\n helper?: ReactNode;\n error?: ReactNode;\n layout: FormLayout;\n size: FormSize;\n}) {\n if (layout.mode === \"inline\") {\n return (\n <div className=\"col-span-2 grid grid-cols-subgrid items-start gap-x-3 gap-y-0.5\">\n <div className={cn(\"flex min-w-0 items-center\", controlMinHeightClass[size])}>{label}</div>\n <div className=\"min-w-0\">{value}</div>\n {helper && <p className=\"col-start-2 text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"col-start-2 text-xs text-destructive\">{error}</p>}\n </div>\n );\n }\n return (\n <div\n className=\"flex w-full flex-col gap-1\"\n style={{ maxWidth: layout.valueMaxWidth ?? \"600px\" }}\n >\n {label}\n {value}\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n\nexport function FieldLabel({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n return (\n <label htmlFor={fieldId} className={cn(\"flex min-w-0 items-center gap-2 font-medium\", labelSizeClass[size])}>\n <LabelIcon icon={field.labelIcon} className=\"shrink-0 text-[15px] text-muted-foreground\" />\n <span className=\"truncate\" title={field.label !== field.key ? field.key : undefined}>\n {field.label}\n </span>\n {field.required && <span className=\"shrink-0 text-destructive\">*</span>}\n {field.badge && (\n <span className=\"shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {field.badge}\n </span>\n )}\n </label>\n );\n}\n\n// ObjectSection renders a nested object as a labelled section: a header row\n// (the field label + required/badge) above its fields, which fill the full\n// width below. It replaces the inline label + bordered box so nested objects\n// read as flat, headed groups rather than indented sub-forms. `col-span-full`\n// makes it span both tracks of an inline FieldsGrid (full width, not crammed\n// into the value column); in a stacked single-column grid it is a no-op.\nexport function ObjectSection({\n label,\n required,\n size,\n badge,\n helper,\n labelIcon,\n children,\n}: {\n label: string;\n required: boolean;\n size: FormSize;\n badge?: string;\n helper?: string;\n labelIcon?: LabelIconSpec;\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"col-span-full flex flex-col\", fieldInnerGapClass[size])}>\n <div className={cn(\"flex items-center gap-2 border-b border-border pb-1 font-semibold\", labelSizeClass[size])}>\n <LabelIcon icon={labelIcon} className=\"text-[15px] text-muted-foreground\" />\n <span>{label}</span>\n {required && <span className=\"text-destructive\">*</span>}\n {badge && (\n <span className=\"rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {badge}\n </span>\n )}\n </div>\n {helper && <p className=\"text-xs text-muted-foreground\">{helper}</p>}\n {children}\n </div>\n );\n}\n\n// ReadOnlyValue renders a `readOnly` field's current value as static text — no\n// input, no border, just the value (date/date-time formatted human-readably).\n// An empty value shows an em-dash. Carries the same id + data-jsf-input as the\n// editable controls so layout and queries stay uniform.\nexport function ReadOnlyValue({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const display = field.kind === \"date\" && text ? formatDateTimeRelative(text) : text;\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {display || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n}\n\n// FieldAdornmentWrapper positions a field's leading/trailing in-field adornments\n// (FieldControl.prefix at the left edge, FieldControl.suffix at the right). The\n// container is tagged data-jsf-control so an adornment can locate its sibling\n// input[data-jsf-input] (e.g. for caret-aware insertion). With neither adornment\n// it renders the input bare, so unadorned fields are byte-for-byte unchanged.\nfunction FieldAdornmentWrapper({\n prefix,\n suffix,\n children,\n}: {\n prefix?: ReactNode;\n suffix?: ReactNode;\n children: ReactNode;\n}) {\n if (!prefix && !suffix) return <>{children}</>;\n return (\n <div data-jsf-control className=\"relative\">\n {prefix && <div className=\"absolute inset-y-0 left-1.5 flex items-center\">{prefix}</div>}\n {children}\n {suffix && <div className=\"absolute inset-y-0 right-1.5 flex items-center\">{suffix}</div>}\n </div>\n );\n}\n\nexport function StringControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <input\n id={fieldId}\n type=\"text\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\nexport function NumberControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // type=\"text\" (not number) so non-numeric values a consumer permits — e.g.\n // template tokens — are not silently dropped by the browser.\n const coerce = field.coerceNumber !== false;\n // A static unit (e.g. \"%\") rides the right edge of the input as a display-only\n // suffix; a consumer-set `suffix` (e.g. an insert-snippet button) wins if both\n // are present (number insert triggers mount on the left as `prefix`).\n const suffix = field.suffix ?? (field.unit ? (\n <span className=\"pointer-events-none select-none text-muted-foreground\">{field.unit}</span>\n ) : undefined);\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={suffix}>\n <input\n id={fieldId}\n type=\"text\"\n inputMode=\"decimal\"\n data-jsf-input\n className={cn(inputClass(size), field.prefix && \"pl-8\", suffix && \"pr-8\")}\n value={toText(field.value)}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => {\n const raw = e.target.value;\n // Coerce mid-typing ONLY when the parse round-trips to the same text\n // (e.g. \"100\", \"33.5\") — so an in-progress decimal like \"33.\" or a\n // trailing-zero \"33.30\" keeps its raw text instead of collapsing to a\n // number and stranding the caret. Non-numeric tokens fall through to\n // the raw string. Blur finalizes any still-uncoerced numeric text.\n if (coerce && raw.trim() !== \"\" && String(Number(raw)) === raw.trim()) {\n field.onChange(Number(raw));\n } else {\n field.onChange(raw);\n }\n }}\n onBlur={(e) => {\n const raw = e.target.value;\n if (coerce && raw.trim() !== \"\" && Number.isFinite(Number(raw))) {\n field.onChange(Number(raw));\n }\n }}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DateControl edits a `format: date`/`date-time` string. Read-only, it shows the\n// human-readable absolute + relative form (e.g. \"Apr 15, 2026, 12:00 PM (2h\n// ago)\"); editable, a DateTimePicker. The raw string is committed unchanged so a\n// consumer-permitted template token in the field is preserved.\nexport function DateControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <div\n id={fieldId}\n data-jsf-input\n className={cn(\"flex items-center text-foreground\", controlHeightClass[size], labelSizeClass[size])}\n title={text || undefined}\n >\n {text ? formatDateTimeRelative(text) : <span className=\"text-muted-foreground\">—</span>}\n </div>\n );\n }\n return (\n <DateTimePicker\n id={fieldId}\n aria-label={field.label}\n data-jsf-input\n inputClassName={cn(inputClass(size), field.prefix && \"pl-8\", field.suffix ? \"pr-14\" : \"pr-8\")}\n value={text}\n onChange={(next) => field.onChange(next)}\n placeholder={defaultPlaceholder(field.schema)}\n prefix={field.prefix}\n suffix={field.suffix}\n />\n );\n}\n\nexport function BooleanControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n // Only render a checkbox when the value is genuinely boolean (or unset).\n // A non-boolean value (e.g. a token a consumer left in place) renders as text\n // so it is preserved and editable.\n if (typeof field.value === \"boolean\" || field.value === undefined || field.value === null) {\n return (\n <div className={cn(\"flex items-center\", controlHeightClass[size])}>\n <input\n id={fieldId}\n type=\"checkbox\"\n className=\"h-4 w-4 accent-primary\"\n checked={field.value === true}\n disabled={readOnly}\n onChange={(e) => field.onChange(e.target.checked)}\n />\n </div>\n );\n }\n return <StringControl field={field} fieldId={fieldId} readOnly={readOnly} size={size} />;\n}\n\nexport function EnumControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const value = toText(field.value);\n const options = withSyntheticValue(field.options ?? [], value);\n if (field.display === \"radio\") {\n return (\n <RadioGroupControl field={field} fieldId={fieldId} readOnly={readOnly} options={options} value={value} size={size} />\n );\n }\n return (\n <Combobox\n id={fieldId}\n options={options}\n value={value}\n disabled={readOnly}\n size={size}\n allowCustomValue={field.allowCustomValue ?? false}\n onChange={(v) => field.onChange(v)}\n prefix={field.prefix}\n suffix={field.suffix}\n {...(defaultPlaceholder(field.schema) ? { placeholder: defaultPlaceholder(field.schema) } : {})}\n />\n );\n}\n\n// RadioGroupControl renders a small fixed enum as a segmented radio-button group\n// instead of a dropdown. It shares EnumControl's option list (any out-of-enum\n// value is already prepended, so a token still shows). One `radiogroup` role +\n// native radios keep it keyboard-navigable; the visible chip is a styled label.\nfunction RadioGroupControl({\n field,\n fieldId,\n readOnly,\n options,\n value,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n options: FieldOption[];\n value: string;\n size: FormSize;\n}) {\n return (\n <div\n role=\"radiogroup\"\n aria-label={field.label}\n id={fieldId}\n data-jsf-input\n className=\"inline-flex flex-wrap items-center gap-1 rounded-md border border-input bg-background p-0.5\"\n >\n {options.map((opt) => {\n const checked = opt.value === value;\n return (\n <label\n key={opt.value}\n className={cn(\n \"inline-flex cursor-pointer select-none items-center rounded px-2.5 py-1\",\n labelSizeClass[size],\n checked\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n readOnly && \"cursor-not-allowed opacity-60\",\n )}\n >\n <input\n type=\"radio\"\n name={fieldId}\n className=\"sr-only\"\n value={opt.value}\n checked={checked}\n disabled={readOnly}\n onChange={() => field.onChange(opt.value)}\n />\n {opt.label}\n </label>\n );\n })}\n </div>\n );\n}\n\n// TextareaControl edits a long-form string in a multi-line box. Read-only, it\n// shows the value with line breaks preserved; editable, a resizable <textarea>.\n// The raw string is committed unchanged so a consumer-permitted template token\n// survives.\nexport function TextareaControl({\n field,\n fieldId,\n readOnly,\n size,\n}: {\n field: FieldControl;\n fieldId: string;\n readOnly: boolean;\n size: FormSize;\n}) {\n const text = toText(field.value);\n if (readOnly) {\n return (\n <span\n id={fieldId}\n data-jsf-input\n data-jsf-readonly\n className={cn(\"block whitespace-pre-wrap break-words text-foreground\", labelSizeClass[size])}\n title={text || undefined}\n >\n {text || <span className=\"text-muted-foreground\">—</span>}\n </span>\n );\n }\n return (\n <FieldAdornmentWrapper prefix={field.prefix} suffix={field.suffix}>\n <textarea\n id={fieldId}\n data-jsf-input\n rows={4}\n className={cn(inputClass(size), \"h-auto min-h-[5rem] resize-y\", field.prefix && \"pl-8\", field.suffix && \"pr-8\")}\n value={text}\n disabled={readOnly}\n placeholder={defaultPlaceholder(field.schema)}\n onChange={(e) => field.onChange(e.target.value)}\n />\n </FieldAdornmentWrapper>\n );\n}\n\n// DisplayControl renders a static, non-editable presentation element:\n// Label/Title (heading), Message (info text), Line (divider), or Blank/Filler\n// (spacer). It never collects a value; the heading/text source is the field's\n// title (label) and, for \"text\", its description. Rendered identically in the\n// form and the read-only Viewer.\nexport function DisplayControl({ field, size }: { field: FieldControl; size: FormSize }) {\n const variant = field.displayVariant ?? \"text\";\n if (variant === \"divider\") {\n return <hr data-jsf-input className=\"my-1 w-full border-t border-border\" />;\n }\n if (variant === \"spacer\") {\n return <div data-jsf-input aria-hidden className=\"h-2\" />;\n }\n if (variant === \"heading\") {\n return (\n <div\n data-jsf-input\n className={cn(\"border-b border-border pb-1 font-semibold text-foreground\", labelSizeClass[size])}\n >\n {field.label}\n </div>\n );\n }\n const text = field.description ?? toText(field.value) ?? field.label;\n return (\n <p data-jsf-input className={cn(\"text-muted-foreground\", labelSizeClass[size])}>\n {text || field.label}\n </p>\n );\n}\n\n// LinkControl renders a read-only external hyperlink. The\n// href is field.href, falling back to the value when it is an absolute URL; with\n// neither it degrades to a plain read-only value.\nexport function LinkControl({ field, fieldId, size }: { field: FieldControl; fieldId: string; size: FormSize }) {\n const text = toText(field.value);\n const href = field.href ?? (/^https?:\\/\\//i.test(text) ? text : undefined);\n if (!href) {\n return <ReadOnlyValue field={field} fieldId={fieldId} size={size} />;\n }\n return (\n <a\n id={fieldId}\n data-jsf-input\n href={href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className={cn(\"inline-flex items-center text-primary underline underline-offset-2\", controlHeightClass[size], labelSizeClass[size])}\n >\n {text || field.label || href}\n </a>\n );\n}\n\n// HMR probe\n// HMR probe 2\n"],"names":[],"mappings":";;;;;;;;;AAkCO,SAAS,WAAW,EAAE,QAAQ,MAAM,YAAyE;AAClH,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,gBAAgB,OAAO,iBAAiB;AAC9C,UAAM,gBAAgB,OAAO,iBAAiB;AAC9C,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW,GAAG,QAAQ,kBAAkB,IAAI,CAAC;AAAA,QAC7C,OAAO,EAAE,qBAAqB,eAAe,aAAa,eAAe,aAAa,IAAA;AAAA,QAErF;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACA,SAAO,oBAAC,SAAI,WAAW,GAAG,QAAQ,mBAAmB,IAAI,CAAC,GAAI,UAAS;AACzE;AAOO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,MAAI,OAAO,SAAS,UAAU;AAC5B,WACE,qBAAC,OAAA,EAAI,WAAU,mEACb,UAAA;AAAA,MAAA,oBAAC,OAAA,EAAI,WAAW,GAAG,6BAA6B,sBAAsB,IAAI,CAAC,GAAI,UAAA,MAAA,CAAM;AAAA,MACrF,oBAAC,OAAA,EAAI,WAAU,WAAW,UAAA,OAAM;AAAA,MAC/B,UAAU,oBAAC,KAAA,EAAE,WAAU,6CAA6C,UAAA,QAAO;AAAA,MAC3E,SAAS,oBAAC,KAAA,EAAE,WAAU,wCAAwC,UAAA,MAAA,CAAM;AAAA,IAAA,GACvE;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,UAAU,OAAO,iBAAiB,QAAA;AAAA,MAE1C,UAAA;AAAA,QAAA;AAAA,QACA;AAAA,QACA,UAAU,oBAAC,KAAA,EAAE,WAAU,iCAAiC,UAAA,QAAO;AAAA,QAC/D,SAAS,oBAAC,KAAA,EAAE,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG/D;AAEO,SAAS,WAAW,EAAE,OAAO,SAAS,QAAkE;AAC7G,SACE,qBAAC,SAAA,EAAM,SAAS,SAAS,WAAW,GAAG,+CAA+C,eAAe,IAAI,CAAC,GACxG,UAAA;AAAA,IAAA,oBAAC,WAAA,EAAU,MAAM,MAAM,WAAW,WAAU,8CAA6C;AAAA,IACzF,oBAAC,QAAA,EAAK,WAAU,YAAW,OAAO,MAAM,UAAU,MAAM,MAAM,MAAM,MAAM,QACvE,gBAAM,OACT;AAAA,IACC,MAAM,YAAY,oBAAC,QAAA,EAAK,WAAU,6BAA4B,UAAA,KAAC;AAAA,IAC/D,MAAM,SACL,oBAAC,UAAK,WAAU,iHACb,gBAAM,MAAA,CACT;AAAA,EAAA,GAEJ;AAEJ;AAQO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,SACE,qBAAC,SAAI,WAAW,GAAG,+BAA+B,mBAAmB,IAAI,CAAC,GACxE,UAAA;AAAA,IAAA,qBAAC,SAAI,WAAW,GAAG,qEAAqE,eAAe,IAAI,CAAC,GAC1G,UAAA;AAAA,MAAA,oBAAC,WAAA,EAAU,MAAM,WAAW,WAAU,qCAAoC;AAAA,MAC1E,oBAAC,UAAM,UAAA,MAAA,CAAM;AAAA,MACZ,YAAY,oBAAC,QAAA,EAAK,WAAU,oBAAmB,UAAA,KAAC;AAAA,MAChD,SACC,oBAAC,QAAA,EAAK,WAAU,wGACb,UAAA,MAAA,CACH;AAAA,IAAA,GAEJ;AAAA,IACC,UAAU,oBAAC,KAAA,EAAE,WAAU,iCAAiC,UAAA,QAAO;AAAA,IAC/D;AAAA,EAAA,GACH;AAEJ;AAMO,SAAS,cAAc,EAAE,OAAO,SAAS,QAAkE;AAChH,QAAM,OAAO,OAAO,MAAM,KAAK;AAC/B,QAAM,UAAU,MAAM,SAAS,UAAU,OAAO,uBAAuB,IAAI,IAAI;AAC/E,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,kBAAc;AAAA,MACd,qBAAiB;AAAA,MACjB,WAAW,GAAG,qCAAqC,mBAAmB,IAAI,GAAG,eAAe,IAAI,CAAC;AAAA,MACjG,OAAO,QAAQ;AAAA,MAEd,UAAA,WAAW,oBAAC,QAAA,EAAK,WAAU,yBAAwB,UAAA,IAAA,CAAC;AAAA,IAAA;AAAA,EAAA;AAG3D;AAOA,SAAS,sBAAsB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,CAAC,UAAU,CAAC,OAAQ,wCAAU,UAAS;AAC3C,SACE,qBAAC,OAAA,EAAI,oBAAgB,MAAC,WAAU,YAC7B,UAAA;AAAA,IAAA,UAAU,oBAAC,OAAA,EAAI,WAAU,iDAAiD,UAAA,QAAO;AAAA,IACjF;AAAA,IACA,UAAU,oBAAC,OAAA,EAAI,WAAU,kDAAkD,UAAA,OAAA,CAAO;AAAA,EAAA,GACrF;AAEJ;AAEO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,6BACG,uBAAA,EAAsB,QAAQ,MAAM,QAAQ,QAAQ,MAAM,QACzD,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,MAAK;AAAA,MACL,kBAAc;AAAA,MACd,WAAW,GAAG,WAAW,IAAI,GAAG,MAAM,UAAU,QAAQ,MAAM,UAAU,MAAM;AAAA,MAC9E,OAAO,OAAO,MAAM,KAAK;AAAA,MACzB,UAAU;AAAA,MACV,aAAa,mBAAmB,MAAM,MAAM;AAAA,MAC5C,UAAU,CAAC,MAAM,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,IAAA;AAAA,EAAA,GAElD;AAEJ;AAEO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AAGD,QAAM,SAAS,MAAM,iBAAiB;AAItC,QAAM,SAAS,MAAM,WAAW,MAAM,OACpC,oBAAC,QAAA,EAAK,WAAU,yDAAyD,UAAA,MAAM,KAAA,CAAK,IAClF;AACJ,SACE,oBAAC,uBAAA,EAAsB,QAAQ,MAAM,QAAQ,QAC3C,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,MAAK;AAAA,MACL,WAAU;AAAA,MACV,kBAAc;AAAA,MACd,WAAW,GAAG,WAAW,IAAI,GAAG,MAAM,UAAU,QAAQ,UAAU,MAAM;AAAA,MACxE,OAAO,OAAO,MAAM,KAAK;AAAA,MACzB,UAAU;AAAA,MACV,aAAa,mBAAmB,MAAM,MAAM;AAAA,MAC5C,UAAU,CAAC,MAAM;AACf,cAAM,MAAM,EAAE,OAAO;AAMrB,YAAI,UAAU,IAAI,KAAA,MAAW,MAAM,OAAO,OAAO,GAAG,CAAC,MAAM,IAAI,KAAA,GAAQ;AACrE,gBAAM,SAAS,OAAO,GAAG,CAAC;AAAA,QAC5B,OAAO;AACL,gBAAM,SAAS,GAAG;AAAA,QACpB;AAAA,MACF;AAAA,MACA,QAAQ,CAAC,MAAM;AACb,cAAM,MAAM,EAAE,OAAO;AACrB,YAAI,UAAU,IAAI,WAAW,MAAM,OAAO,SAAS,OAAO,GAAG,CAAC,GAAG;AAC/D,gBAAM,SAAS,OAAO,GAAG,CAAC;AAAA,QAC5B;AAAA,MACF;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;AAMO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,OAAO,OAAO,MAAM,KAAK;AAC/B,MAAI,UAAU;AACZ,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAI;AAAA,QACJ,kBAAc;AAAA,QACd,WAAW,GAAG,qCAAqC,mBAAmB,IAAI,GAAG,eAAe,IAAI,CAAC;AAAA,QACjG,OAAO,QAAQ;AAAA,QAEd,UAAA,OAAO,uBAAuB,IAAI,wBAAK,QAAA,EAAK,WAAU,yBAAwB,UAAA,IAAA,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EAGtF;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,cAAY,MAAM;AAAA,MAClB,kBAAc;AAAA,MACd,gBAAgB,GAAG,WAAW,IAAI,GAAG,MAAM,UAAU,QAAQ,MAAM,SAAS,UAAU,MAAM;AAAA,MAC5F,OAAO;AAAA,MACP,UAAU,CAAC,SAAS,MAAM,SAAS,IAAI;AAAA,MACvC,aAAa,mBAAmB,MAAM,MAAM;AAAA,MAC5C,QAAQ,MAAM;AAAA,MACd,QAAQ,MAAM;AAAA,IAAA;AAAA,EAAA;AAGpB;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AAID,MAAI,OAAO,MAAM,UAAU,aAAa,MAAM,UAAU,UAAa,MAAM,UAAU,MAAM;AACzF,WACE,oBAAC,SAAI,WAAW,GAAG,qBAAqB,mBAAmB,IAAI,CAAC,GAC9D,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAI;AAAA,QACJ,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,UAAU;AAAA,QACzB,UAAU;AAAA,QACV,UAAU,CAAC,MAAM,MAAM,SAAS,EAAE,OAAO,OAAO;AAAA,MAAA;AAAA,IAAA,GAEpD;AAAA,EAEJ;AACA,SAAO,oBAAC,eAAA,EAAc,OAAc,SAAkB,UAAoB,MAAY;AACxF;AAEO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,QAAQ,OAAO,MAAM,KAAK;AAChC,QAAM,UAAU,mBAAmB,MAAM,WAAW,CAAA,GAAI,KAAK;AAC7D,MAAI,MAAM,YAAY,SAAS;AAC7B,+BACG,mBAAA,EAAkB,OAAc,SAAkB,UAAoB,SAAkB,OAAc,MAAY;AAAA,EAEvH;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV;AAAA,MACA,kBAAkB,MAAM,oBAAoB;AAAA,MAC5C,UAAU,CAAC,MAAM,MAAM,SAAS,CAAC;AAAA,MACjC,QAAQ,MAAM;AAAA,MACd,QAAQ,MAAM;AAAA,MACb,GAAI,mBAAmB,MAAM,MAAM,IAAI,EAAE,aAAa,mBAAmB,MAAM,MAAM,MAAM,CAAA;AAAA,IAAC;AAAA,EAAA;AAGnG;AAMA,SAAS,kBAAkB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,MAAM;AAAA,MAClB,IAAI;AAAA,MACJ,kBAAc;AAAA,MACd,WAAU;AAAA,MAET,UAAA,QAAQ,IAAI,CAAC,QAAQ;AACpB,cAAM,UAAU,IAAI,UAAU;AAC9B,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,WAAW;AAAA,cACT;AAAA,cACA,eAAe,IAAI;AAAA,cACnB,UACI,uCACA;AAAA,cACJ,YAAY;AAAA,YAAA;AAAA,YAGd,UAAA;AAAA,cAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,WAAU;AAAA,kBACV,OAAO,IAAI;AAAA,kBACX;AAAA,kBACA,UAAU;AAAA,kBACV,UAAU,MAAM,MAAM,SAAS,IAAI,KAAK;AAAA,gBAAA;AAAA,cAAA;AAAA,cAEzC,IAAI;AAAA,YAAA;AAAA,UAAA;AAAA,UAnBA,IAAI;AAAA,QAAA;AAAA,MAsBf,CAAC;AAAA,IAAA;AAAA,EAAA;AAGP;AAMO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,OAAO,OAAO,MAAM,KAAK;AAC/B,MAAI,UAAU;AACZ,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAI;AAAA,QACJ,kBAAc;AAAA,QACd,qBAAiB;AAAA,QACjB,WAAW,GAAG,yDAAyD,eAAe,IAAI,CAAC;AAAA,QAC3F,OAAO,QAAQ;AAAA,QAEd,UAAA,QAAQ,oBAAC,QAAA,EAAK,WAAU,yBAAwB,UAAA,IAAA,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EAGxD;AACA,6BACG,uBAAA,EAAsB,QAAQ,MAAM,QAAQ,QAAQ,MAAM,QACzD,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,kBAAc;AAAA,MACd,MAAM;AAAA,MACN,WAAW,GAAG,WAAW,IAAI,GAAG,gCAAgC,MAAM,UAAU,QAAQ,MAAM,UAAU,MAAM;AAAA,MAC9G,OAAO;AAAA,MACP,UAAU;AAAA,MACV,aAAa,mBAAmB,MAAM,MAAM;AAAA,MAC5C,UAAU,CAAC,MAAM,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,IAAA;AAAA,EAAA,GAElD;AAEJ;AAOO,SAAS,eAAe,EAAE,OAAO,QAAiD;AACvF,QAAM,UAAU,MAAM,kBAAkB;AACxC,MAAI,YAAY,WAAW;AACzB,WAAO,oBAAC,MAAA,EAAG,kBAAc,MAAC,WAAU,sCAAqC;AAAA,EAC3E;AACA,MAAI,YAAY,UAAU;AACxB,+BAAQ,OAAA,EAAI,kBAAc,MAAC,eAAW,MAAC,WAAU,OAAM;AAAA,EACzD;AACA,MAAI,YAAY,WAAW;AACzB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,kBAAc;AAAA,QACd,WAAW,GAAG,6DAA6D,eAAe,IAAI,CAAC;AAAA,QAE9F,UAAA,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAGb;AACA,QAAM,OAAO,MAAM,eAAe,OAAO,MAAM,KAAK,KAAK,MAAM;AAC/D,SACE,oBAAC,KAAA,EAAE,kBAAc,MAAC,WAAW,GAAG,yBAAyB,eAAe,IAAI,CAAC,GAC1E,UAAA,QAAQ,MAAM,OACjB;AAEJ;AAKO,SAAS,YAAY,EAAE,OAAO,SAAS,QAAkE;AAC9G,QAAM,OAAO,OAAO,MAAM,KAAK;AAC/B,QAAM,OAAO,MAAM,SAAS,gBAAgB,KAAK,IAAI,IAAI,OAAO;AAChE,MAAI,CAAC,MAAM;AACT,WAAO,oBAAC,eAAA,EAAc,OAAc,SAAkB,KAAA,CAAY;AAAA,EACpE;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,kBAAc;AAAA,MACd;AAAA,MACA,QAAO;AAAA,MACP,KAAI;AAAA,MACJ,WAAW,GAAG,sEAAsE,mBAAmB,IAAI,GAAG,eAAe,IAAI,CAAC;AAAA,MAEjI,UAAA,QAAQ,MAAM,SAAS;AAAA,IAAA;AAAA,EAAA;AAG9B;"}