@pipe0/react 1.0.36 → 1.0.38

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 (96) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/compound/effect-catalog/category-filter.mjs +1 -1
  3. package/dist/components/compound/effect-catalog/category-filter.mjs.map +1 -1
  4. package/dist/components/compound/effect-catalog/search-filter.mjs +1 -1
  5. package/dist/components/compound/effect-catalog/search-filter.mjs.map +1 -1
  6. package/dist/components/compound/pipe-catalog/category-filter.mjs +1 -1
  7. package/dist/components/compound/pipe-catalog/category-filter.mjs.map +1 -1
  8. package/dist/components/compound/pipe-catalog/input-field-filter.mjs +1 -1
  9. package/dist/components/compound/pipe-catalog/input-field-filter.mjs.map +1 -1
  10. package/dist/components/compound/pipe-catalog/output-field-filter.mjs +1 -1
  11. package/dist/components/compound/pipe-catalog/output-field-filter.mjs.map +1 -1
  12. package/dist/components/compound/pipe-catalog/provider-filter.mjs +1 -1
  13. package/dist/components/compound/pipe-catalog/provider-filter.mjs.map +1 -1
  14. package/dist/components/compound/pipe-catalog/search-filter.mjs +1 -1
  15. package/dist/components/compound/pipe-catalog/search-filter.mjs.map +1 -1
  16. package/dist/components/compound/pipe-catalog/tag-filter.mjs +1 -1
  17. package/dist/components/compound/pipe-catalog/tag-filter.mjs.map +1 -1
  18. package/dist/components/compound/search-catalog/category-filter.mjs +1 -1
  19. package/dist/components/compound/search-catalog/category-filter.mjs.map +1 -1
  20. package/dist/components/compound/search-catalog/output-field-filter.mjs +1 -1
  21. package/dist/components/compound/search-catalog/output-field-filter.mjs.map +1 -1
  22. package/dist/components/compound/search-catalog/provider-filter.mjs +1 -1
  23. package/dist/components/compound/search-catalog/provider-filter.mjs.map +1 -1
  24. package/dist/components/compound/search-catalog/search-filter.mjs +1 -1
  25. package/dist/components/compound/search-catalog/search-filter.mjs.map +1 -1
  26. package/dist/components/compound/search-catalog/tag-filter.mjs +1 -1
  27. package/dist/components/compound/search-catalog/tag-filter.mjs.map +1 -1
  28. package/dist/components/defaults/adapters/json-extraction-input.mjs +1 -1
  29. package/dist/components/defaults/adapters/loose-object-input.mjs +1 -1
  30. package/dist/components/defaults/adapters/loose-object-input.mjs.map +1 -1
  31. package/dist/components/defaults/adapters/pipes-run-if-input.mjs +1 -1
  32. package/dist/components/defaults/adapters/prompt-input.mjs +1 -1
  33. package/dist/components/defaults/adapters/property-list-input.mjs +32 -16
  34. package/dist/components/defaults/adapters/property-list-input.mjs.map +1 -1
  35. package/dist/components/defaults/adapters/providers-input.mjs +1 -2
  36. package/dist/components/defaults/adapters/providers-input.mjs.map +1 -1
  37. package/dist/components/defaults/adapters/select-input.mjs +1 -1
  38. package/dist/components/defaults/adapters/select-input.mjs.map +1 -1
  39. package/dist/components/defaults/adapters/tagged-ordered-multi-create-input.mjs +2 -3
  40. package/dist/components/defaults/adapters/tagged-ordered-multi-create-input.mjs.map +1 -1
  41. package/dist/components/defaults/adapters/tagged-text-input.mjs +1 -1
  42. package/dist/components/defaults/adapters/tagged-text-input.mjs.map +1 -1
  43. package/dist/components/defaults/catalog/active-filter-pill.mjs +1 -1
  44. package/dist/components/defaults/catalog/active-filter-pill.mjs.map +1 -1
  45. package/dist/components/defaults/catalog/card-derived.d.mts.map +1 -1
  46. package/dist/components/defaults/catalog/card-derived.mjs +9 -6
  47. package/dist/components/defaults/catalog/card-derived.mjs.map +1 -1
  48. package/dist/components/defaults/catalog/category-section.d.mts +2 -2
  49. package/dist/components/defaults/catalog/category-section.d.mts.map +1 -1
  50. package/dist/components/defaults/catalog/category-section.mjs +10 -15
  51. package/dist/components/defaults/catalog/category-section.mjs.map +1 -1
  52. package/dist/components/defaults/catalog/provider-avatars.mjs +1 -1
  53. package/dist/components/defaults/catalog/provider-avatars.mjs.map +1 -1
  54. package/dist/components/defaults/layout/field-wrapper.mjs +2 -2
  55. package/dist/components/defaults/layout/field-wrapper.mjs.map +1 -1
  56. package/dist/components/defaults/layout/group.mjs +2 -1
  57. package/dist/components/defaults/layout/group.mjs.map +1 -1
  58. package/dist/components/defaults/layout/section.mjs +5 -8
  59. package/dist/components/defaults/layout/section.mjs.map +1 -1
  60. package/dist/components/internal/LiquidEditor/ChipEditPopover.mjs +1 -1
  61. package/dist/components/internal/LiquidEditor/ChipEditPopover.mjs.map +1 -1
  62. package/dist/components/internal/LiquidEditor/UnifiedReferencePicker.mjs +1 -1
  63. package/dist/components/internal/LiquidEditor/UnifiedReferencePicker.mjs.map +1 -1
  64. package/dist/components/internal/combobox/include-exclude-combobox.mjs +2 -3
  65. package/dist/components/internal/combobox/include-exclude-combobox.mjs.map +1 -1
  66. package/dist/components/internal/combobox/suggest-combobox.mjs +2 -3
  67. package/dist/components/internal/combobox/suggest-combobox.mjs.map +1 -1
  68. package/dist/components/internal/icons.d.mts +14 -0
  69. package/dist/components/internal/icons.d.mts.map +1 -0
  70. package/dist/components/internal/icons.mjs +137 -185
  71. package/dist/components/internal/icons.mjs.map +1 -1
  72. package/dist/components/ui/combobox.mjs +4 -4
  73. package/dist/components/ui/combobox.mjs.map +1 -1
  74. package/dist/components/ui/select.mjs +5 -5
  75. package/dist/components/ui/select.mjs.map +1 -1
  76. package/dist/hooks/use-pipe-catalog-table.d.mts +8 -8
  77. package/dist/hooks/use-search-catalog-table.d.mts +6 -6
  78. package/dist/styles/pipe0-form.css +1 -1
  79. package/dist/utils/catalog-helpers.d.mts +4 -5
  80. package/dist/utils/catalog-helpers.d.mts.map +1 -1
  81. package/dist/utils/catalog-helpers.mjs +4 -4
  82. package/dist/utils/catalog-helpers.mjs.map +1 -1
  83. package/dist/widgets/avatar-group.d.mts +12 -3
  84. package/dist/widgets/avatar-group.d.mts.map +1 -1
  85. package/dist/widgets/avatar-group.mjs +173 -50
  86. package/dist/widgets/avatar-group.mjs.map +1 -1
  87. package/dist/widgets/field-type-badge.d.mts.map +1 -1
  88. package/dist/widgets/field-type-badge.mjs +1 -1
  89. package/dist/widgets/field-type-badge.mjs.map +1 -1
  90. package/dist/widgets/icon-glyph.mjs +2 -3
  91. package/dist/widgets/icon-glyph.mjs.map +1 -1
  92. package/dist/widgets/token-pricing-badge.mjs +1 -1
  93. package/dist/widgets/token-pricing-badge.mjs.map +1 -1
  94. package/package.json +4 -3
  95. package/dist/widgets/glyphs/linkedin.mjs +0 -32
  96. package/dist/widgets/glyphs/linkedin.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"category-section.mjs","names":[],"sources":["../../../../src/components/defaults/catalog/category-section.tsx"],"sourcesContent":["import { ChevronDown } from \"lucide-react\";\nimport { type ReactNode, useState } from \"react\";\nimport { cn } from \"../../../lib/utils.js\";\n\nexport interface CatalogCategorySectionProps {\n /** Display label (e.g. \"People\"). */\n label: ReactNode;\n /** Hex / CSS color used for the small leading dot. */\n color?: string;\n /** Count rendered next to the label. */\n count?: number;\n /** Default open state when uncontrolled. Defaults to `true`. */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n className?: string;\n /** Class names applied to the body container that wraps `children`. */\n bodyClassName?: string;\n children?: ReactNode;\n}\n\n/**\n * Collapsible category section header. Used to group catalog cards by\n * category. Click the header (or chevron) to toggle the body.\n */\nexport function CatalogCategorySection({\n label,\n color,\n count,\n defaultOpen = true,\n open,\n onOpenChange,\n className,\n bodyClassName,\n children,\n}: CatalogCategorySectionProps) {\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isControlled = open !== undefined;\n const isOpen = isControlled ? !!open : internalOpen;\n\n const toggle = () => {\n const next = !isOpen;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n };\n\n return (\n <section\n data-p0=\"catalog-category-section\"\n data-open={isOpen ? \"true\" : \"false\"}\n className={cn(\"pz:flex pz:flex-col\", className)}\n >\n <button\n type=\"button\"\n data-p0=\"catalog-category-section-header\"\n onClick={toggle}\n aria-expanded={isOpen}\n className=\"pz:flex pz:items-center pz:gap-2 pz:py-2 pz:text-left pz:bg-transparent pz:border-0 pz:border-b pz:border-input pz:cursor-pointer pz:transition-colors pz:hover:bg-muted/30\"\n >\n <ChevronDown\n aria-hidden=\"true\"\n className={cn(\n \"pz:size-3.5 pz:text-muted-foreground pz:shrink-0 pz:transition-transform\",\n isOpen ? undefined : \"pz:-rotate-90\",\n )}\n />\n {color ? (\n <span\n aria-hidden=\"true\"\n className=\"pz:size-1.5 pz:rounded-full pz:shrink-0\"\n style={{ backgroundColor: color }}\n />\n ) : null}\n <span className=\"pz:text-[11px] pz:font-semibold pz:tracking-[0.08em] pz:uppercase pz:text-foreground\">\n {label}\n </span>\n {typeof count === \"number\" ? (\n <span className=\"pz:text-[11px] pz:text-muted-foreground\">{count}</span>\n ) : null}\n </button>\n {isOpen ? (\n <div\n data-p0=\"catalog-category-section-body\"\n className={cn(\"pz:flex pz:flex-col\", bodyClassName)}\n >\n {children}\n </div>\n ) : null}\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;AA0BA,SAAgB,uBAAuB,EACrC,OACA,OACA,OACA,cAAc,MACd,MACA,cACA,WACA,eACA,YAC8B;CAC9B,MAAM,CAAC,cAAc,mBAAmB,SAAS,YAAY;CAC7D,MAAM,eAAe,SAAS;CAC9B,MAAM,SAAS,eAAe,CAAC,CAAC,OAAO;CAEvC,MAAM,eAAe;EACnB,MAAM,OAAO,CAAC;AACd,MAAI,CAAC,aAAc,iBAAgB,KAAK;AACxC,iBAAe,KAAK;;AAGtB,QACE,qBAAC,WAAD;EACE,WAAQ;EACR,aAAW,SAAS,SAAS;EAC7B,WAAW,GAAG,uBAAuB,UAAU;YAHjD,CAKE,qBAAC,UAAD;GACE,MAAK;GACL,WAAQ;GACR,SAAS;GACT,iBAAe;GACf,WAAU;aALZ;IAOE,oBAAC,aAAD;KACE,eAAY;KACZ,WAAW,GACT,4EACA,SAAS,SAAY,gBACtB;KACD;IACD,QACC,oBAAC,QAAD;KACE,eAAY;KACZ,WAAU;KACV,OAAO,EAAE,iBAAiB,OAAO;KACjC,IACA;IACJ,oBAAC,QAAD;KAAM,WAAU;eACb;KACI;IACN,OAAO,UAAU,WAChB,oBAAC,QAAD;KAAM,WAAU;eAA2C;KAAa,IACtE;IACG;MACR,SACC,oBAAC,OAAD;GACE,WAAQ;GACR,WAAW,GAAG,uBAAuB,cAAc;GAElD;GACG,IACJ,KACI"}
1
+ {"version":3,"file":"category-section.mjs","names":[],"sources":["../../../../src/components/defaults/catalog/category-section.tsx"],"sourcesContent":["import { type ReactNode, useState } from \"react\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { ChevronDown } from \"../../internal/icons.js\";\n\nexport interface CatalogCategorySectionProps {\n /** Display label (e.g. \"People\"). */\n label: ReactNode;\n /** @deprecated No longer rendered — sections carry no colour dot. Kept so\n * existing callers keep compiling. */\n color?: string;\n /** Count rendered next to the label. */\n count?: number;\n /** Default open state when uncontrolled. Defaults to `true`. */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n className?: string;\n /** Class names applied to the body container that wraps `children`. */\n bodyClassName?: string;\n children?: ReactNode;\n}\n\n/**\n * Collapsible category section header. Used to group catalog cards by\n * category. Click the header (or chevron) to toggle the body.\n */\nexport function CatalogCategorySection({\n label,\n count,\n defaultOpen = true,\n open,\n onOpenChange,\n className,\n bodyClassName,\n children,\n}: CatalogCategorySectionProps) {\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isControlled = open !== undefined;\n const isOpen = isControlled ? !!open : internalOpen;\n\n const toggle = () => {\n const next = !isOpen;\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n };\n\n return (\n <section\n data-p0=\"catalog-category-section\"\n data-open={isOpen ? \"true\" : \"false\"}\n className={cn(\"pz:flex pz:flex-col\", className)}\n >\n <button\n type=\"button\"\n data-p0=\"catalog-category-section-header\"\n onClick={toggle}\n aria-expanded={isOpen}\n // A sentence-case heading with a boxed count — no rule, no colour\n // dot: the space before the first card separates the section.\n className=\"pz:group/cat pz:flex pz:items-center pz:gap-2 pz:self-start pz:rounded-md pz:py-1 pz:text-left pz:bg-transparent pz:border-0 pz:cursor-pointer\"\n >\n <span className=\"pz:text-sm pz:font-semibold pz:tracking-[-0.006em] pz:text-foreground\">\n {label}\n </span>\n {typeof count === \"number\" ? (\n <span className=\"pz:inline-flex pz:h-4 pz:min-w-4 pz:items-center pz:justify-center pz:rounded-[4px] pz:bg-muted pz:px-1 pz:text-[10.5px] pz:font-semibold pz:tabular-nums pz:text-muted-foreground pz:ring-1 pz:ring-border\">\n {count}\n </span>\n ) : null}\n <ChevronDown\n aria-hidden=\"true\"\n className={cn(\n \"pz:size-3.5 pz:text-muted-foreground pz:shrink-0 pz:transition-transform pz:group-hover/cat:text-foreground\",\n isOpen ? undefined : \"pz:-rotate-90\",\n )}\n />\n </button>\n {isOpen ? (\n <div\n data-p0=\"catalog-category-section-body\"\n className={cn(\"pz:flex pz:flex-col\", bodyClassName)}\n >\n {children}\n </div>\n ) : null}\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;AA2BA,SAAgB,uBAAuB,EACrC,OACA,OACA,cAAc,MACd,MACA,cACA,WACA,eACA,YAC8B;CAC9B,MAAM,CAAC,cAAc,mBAAmB,SAAS,YAAY;CAC7D,MAAM,eAAe,SAAS;CAC9B,MAAM,SAAS,eAAe,CAAC,CAAC,OAAO;CAEvC,MAAM,eAAe;EACnB,MAAM,OAAO,CAAC;AACd,MAAI,CAAC,aAAc,iBAAgB,KAAK;AACxC,iBAAe,KAAK;;AAGtB,QACE,qBAAC,WAAD;EACE,WAAQ;EACR,aAAW,SAAS,SAAS;EAC7B,WAAW,GAAG,uBAAuB,UAAU;YAHjD,CAKE,qBAAC,UAAD;GACE,MAAK;GACL,WAAQ;GACR,SAAS;GACT,iBAAe;GAGf,WAAU;aAPZ;IASE,oBAAC,QAAD;KAAM,WAAU;eACb;KACI;IACN,OAAO,UAAU,WAChB,oBAAC,QAAD;KAAM,WAAU;eACb;KACI,IACL;IACJ,oBAAC,aAAD;KACE,eAAY;KACZ,WAAW,GACT,+GACA,SAAS,SAAY,gBACtB;KACD;IACK;MACR,SACC,oBAAC,OAAD;GACE,WAAQ;GACR,WAAW,GAAG,uBAAuB,cAAc;GAElD;GACG,IACJ,KACI"}
@@ -1,10 +1,10 @@
1
1
  import { usePortalContainer } from "../../../context/portal-container-context.mjs";
2
2
  import { cn } from "../../../lib/utils.mjs";
3
3
  import { useAnyCatalogCardOptional } from "../../../context/catalog-card-context.mjs";
4
+ import { Coins } from "../../internal/icons.mjs";
4
5
  import { AvatarGroup } from "../../../widgets/avatar-group.mjs";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { getProviderEntry } from "@pipe0/base";
7
- import { Coins } from "lucide-react";
8
8
  import { PreviewCard } from "@base-ui/react/preview-card";
9
9
 
10
10
  //#region src/components/defaults/catalog/provider-avatars.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"provider-avatars.mjs","names":["PreviewCardPrimitive"],"sources":["../../../../src/components/defaults/catalog/provider-avatars.tsx"],"sourcesContent":["import { PreviewCard as PreviewCardPrimitive } from \"@base-ui/react/preview-card\";\nimport { getProviderEntry, type ProviderName } from \"@pipe0/base\";\nimport { Coins } from \"lucide-react\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useAnyCatalogCardOptional } from \"../../../context/catalog-card-context.js\";\nimport { usePortalContainer } from \"../../../context/portal-container-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { AvatarGroup, type AvatarGroupProps } from \"../../../widgets/avatar-group.js\";\n\nexport interface CatalogProviderAvatarsProps\n extends Omit<AvatarGroupProps, \"providers\" | \"render\"> {\n /** Override the providers list. Defaults to `card.providers` from card context. */\n providers?: readonly ProviderName[];\n /** Override the cost lookup. Defaults to `card.startingCostPerProvider` from card context. */\n startingCostPerProvider?: Partial<Record<ProviderName, number>>;\n /** When true, the hover popover does not appear — pure visual stack. */\n disableHover?: boolean;\n /** Fully replace the popover body. */\n renderPopover?: (state: {\n providers: readonly ProviderName[];\n startingCostPerProvider?: Partial<Record<ProviderName, number>>;\n }) => ReactNode;\n}\n\n/**\n * `<AvatarGroup>` with a hover popover that lists each provider plus its\n * starting cost. Self-binds to the surrounding catalog card context — pass\n * explicit `providers` to override.\n */\nexport function CatalogProviderAvatars({\n providers,\n startingCostPerProvider,\n disableHover,\n renderPopover,\n size,\n showCount,\n className,\n ...rest\n}: CatalogProviderAvatarsProps) {\n const card = useAnyCatalogCardOptional()?.card;\n const portalContainer = usePortalContainer();\n const resolvedProviders =\n providers ??\n (card && \"providers\" in card ? (card.providers as ProviderName[] | undefined) : undefined) ??\n [];\n const resolvedCosts =\n startingCostPerProvider ??\n (card && \"startingCostPerProvider\" in card ? card.startingCostPerProvider : undefined);\n\n if (!resolvedProviders.length) return null;\n\n if (disableHover) {\n return (\n <AvatarGroup\n providers={resolvedProviders}\n size={size}\n showCount={showCount}\n className={className}\n onClick={(e) => e.stopPropagation()}\n {...rest}\n />\n );\n }\n\n return (\n <PreviewCardPrimitive.Root>\n <PreviewCardPrimitive.Trigger\n render={(triggerProps) => (\n <AvatarGroup\n providers={resolvedProviders}\n size={size}\n showCount={showCount}\n className={cn(\"pz:cursor-pointer\", className)}\n {...rest}\n {...(triggerProps as Record<string, unknown>)}\n onClick={(e: MouseEvent<HTMLDivElement>) => {\n e.stopPropagation();\n const triggerOnClick = (\n triggerProps as {\n onClick?: (e: MouseEvent<HTMLDivElement>) => void;\n }\n ).onClick;\n triggerOnClick?.(e);\n }}\n />\n )}\n />\n <PreviewCardPrimitive.Portal container={portalContainer}>\n <PreviewCardPrimitive.Positioner\n side=\"bottom\"\n sideOffset={4}\n className=\"pz:isolate pz:z-50\"\n >\n <PreviewCardPrimitive.Popup\n data-p0=\"catalog-provider-avatars-popup\"\n className=\"pz:rounded-lg pz:bg-popover pz:text-popover-foreground pz:p-2.5 pz:shadow-md pz:ring-1 pz:ring-foreground/10 pz:outline-none pz:w-56\"\n >\n {renderPopover ? (\n renderPopover({\n providers: resolvedProviders,\n startingCostPerProvider: resolvedCosts,\n })\n ) : (\n <DefaultPopoverBody\n providers={resolvedProviders}\n startingCostPerProvider={resolvedCosts}\n />\n )}\n </PreviewCardPrimitive.Popup>\n </PreviewCardPrimitive.Positioner>\n </PreviewCardPrimitive.Portal>\n </PreviewCardPrimitive.Root>\n );\n}\n\nfunction DefaultPopoverBody({\n providers,\n startingCostPerProvider,\n}: {\n providers: readonly ProviderName[];\n startingCostPerProvider?: Partial<Record<ProviderName, number>>;\n}) {\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-1.5\">\n <h4 className=\"pz:m-0 pz:text-sm pz:font-medium\">Providers</h4>\n <ul className=\"pz:flex pz:flex-col pz:gap-1.5 pz:list-none pz:m-0 pz:p-0\">\n {providers.map((provider) => {\n const entry = getProviderEntry(provider);\n const cost = startingCostPerProvider?.[provider];\n return (\n <li key={provider} className=\"pz:flex pz:items-center pz:gap-2\">\n <img\n src={entry?.logoUrl ?? \"/placeholder.svg?height=24&width=24\"}\n alt={entry?.label ?? provider}\n className=\"pz:rounded-md pz:h-6 pz:w-6 pz:object-cover pz:bg-background pz:border pz:border-input\"\n />\n <span className=\"pz:text-sm pz:flex-1 pz:truncate\">{entry?.label ?? provider}</span>\n {startingCostPerProvider ? (\n <span className=\"pz:inline-flex pz:items-center pz:gap-1 pz:text-xs pz:text-muted-foreground\">\n <Coins className=\"pz:size-3\" />\n {cost ?? \"Free\"}\n </span>\n ) : null}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6BA,SAAgB,uBAAuB,EACrC,WACA,yBACA,cACA,eACA,MACA,WACA,WACA,GAAG,QAC2B;CAC9B,MAAM,OAAO,2BAA2B,EAAE;CAC1C,MAAM,kBAAkB,oBAAoB;CAC5C,MAAM,oBACJ,cACC,QAAQ,eAAe,OAAQ,KAAK,YAA2C,WAChF,EAAE;CACJ,MAAM,gBACJ,4BACC,QAAQ,6BAA6B,OAAO,KAAK,0BAA0B;AAE9E,KAAI,CAAC,kBAAkB,OAAQ,QAAO;AAEtC,KAAI,aACF,QACE,oBAAC,aAAD;EACE,WAAW;EACL;EACK;EACA;EACX,UAAU,MAAM,EAAE,iBAAiB;EACnC,GAAI;EACJ;AAIN,QACE,qBAACA,YAAqB,MAAtB,aACE,oBAACA,YAAqB,SAAtB,EACE,SAAS,iBACP,oBAAC,aAAD;EACE,WAAW;EACL;EACK;EACX,WAAW,GAAG,qBAAqB,UAAU;EAC7C,GAAI;EACJ,GAAK;EACL,UAAU,MAAkC;AAC1C,KAAE,iBAAiB;GACnB,MAAM,iBACJ,aAGA;AACF,oBAAiB,EAAE;;EAErB,GAEJ,GACF,oBAACA,YAAqB,QAAtB;EAA6B,WAAW;YACtC,oBAACA,YAAqB,YAAtB;GACE,MAAK;GACL,YAAY;GACZ,WAAU;aAEV,oBAACA,YAAqB,OAAtB;IACE,WAAQ;IACR,WAAU;cAET,gBACC,cAAc;KACZ,WAAW;KACX,yBAAyB;KAC1B,CAAC,GAEF,oBAAC,oBAAD;KACE,WAAW;KACX,yBAAyB;KACzB;IAEuB;GACG;EACN,EACJ;;AAIhC,SAAS,mBAAmB,EAC1B,WACA,2BAIC;AACD,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,MAAD;GAAI,WAAU;aAAmC;GAAc,GAC/D,oBAAC,MAAD;GAAI,WAAU;aACX,UAAU,KAAK,aAAa;IAC3B,MAAM,QAAQ,iBAAiB,SAAS;IACxC,MAAM,OAAO,0BAA0B;AACvC,WACE,qBAAC,MAAD;KAAmB,WAAU;eAA7B;MACE,oBAAC,OAAD;OACE,KAAK,OAAO,WAAW;OACvB,KAAK,OAAO,SAAS;OACrB,WAAU;OACV;MACF,oBAAC,QAAD;OAAM,WAAU;iBAAoC,OAAO,SAAS;OAAgB;MACnF,0BACC,qBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,oBAAC,OAAD,EAAO,WAAU,aAAc,GAC9B,QAAQ,OACJ;WACL;MACD;OAbI,SAaJ;KAEP;GACC,EACD"}
1
+ {"version":3,"file":"provider-avatars.mjs","names":["PreviewCardPrimitive"],"sources":["../../../../src/components/defaults/catalog/provider-avatars.tsx"],"sourcesContent":["import { PreviewCard as PreviewCardPrimitive } from \"@base-ui/react/preview-card\";\nimport { getProviderEntry, type ProviderName } from \"@pipe0/base\";\nimport type { MouseEvent, ReactNode } from \"react\";\nimport { useAnyCatalogCardOptional } from \"../../../context/catalog-card-context.js\";\nimport { usePortalContainer } from \"../../../context/portal-container-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { AvatarGroup, type AvatarGroupProps } from \"../../../widgets/avatar-group.js\";\nimport { Coins } from \"../../internal/icons.js\";\n\nexport interface CatalogProviderAvatarsProps\n extends Omit<AvatarGroupProps, \"providers\" | \"render\"> {\n /** Override the providers list. Defaults to `card.providers` from card context. */\n providers?: readonly ProviderName[];\n /** Override the cost lookup. Defaults to `card.startingCostPerProvider` from card context. */\n startingCostPerProvider?: Partial<Record<ProviderName, number>>;\n /** When true, the hover popover does not appear — pure visual stack. */\n disableHover?: boolean;\n /** Fully replace the popover body. */\n renderPopover?: (state: {\n providers: readonly ProviderName[];\n startingCostPerProvider?: Partial<Record<ProviderName, number>>;\n }) => ReactNode;\n}\n\n/**\n * `<AvatarGroup>` with a hover popover that lists each provider plus its\n * starting cost. Self-binds to the surrounding catalog card context — pass\n * explicit `providers` to override.\n */\nexport function CatalogProviderAvatars({\n providers,\n startingCostPerProvider,\n disableHover,\n renderPopover,\n size,\n showCount,\n className,\n ...rest\n}: CatalogProviderAvatarsProps) {\n const card = useAnyCatalogCardOptional()?.card;\n const portalContainer = usePortalContainer();\n const resolvedProviders =\n providers ??\n (card && \"providers\" in card ? (card.providers as ProviderName[] | undefined) : undefined) ??\n [];\n const resolvedCosts =\n startingCostPerProvider ??\n (card && \"startingCostPerProvider\" in card ? card.startingCostPerProvider : undefined);\n\n if (!resolvedProviders.length) return null;\n\n if (disableHover) {\n return (\n <AvatarGroup\n providers={resolvedProviders}\n size={size}\n showCount={showCount}\n className={className}\n onClick={(e) => e.stopPropagation()}\n {...rest}\n />\n );\n }\n\n return (\n <PreviewCardPrimitive.Root>\n <PreviewCardPrimitive.Trigger\n render={(triggerProps) => (\n <AvatarGroup\n providers={resolvedProviders}\n size={size}\n showCount={showCount}\n className={cn(\"pz:cursor-pointer\", className)}\n {...rest}\n {...(triggerProps as Record<string, unknown>)}\n onClick={(e: MouseEvent<HTMLDivElement>) => {\n e.stopPropagation();\n const triggerOnClick = (\n triggerProps as {\n onClick?: (e: MouseEvent<HTMLDivElement>) => void;\n }\n ).onClick;\n triggerOnClick?.(e);\n }}\n />\n )}\n />\n <PreviewCardPrimitive.Portal container={portalContainer}>\n <PreviewCardPrimitive.Positioner\n side=\"bottom\"\n sideOffset={4}\n className=\"pz:isolate pz:z-50\"\n >\n <PreviewCardPrimitive.Popup\n data-p0=\"catalog-provider-avatars-popup\"\n className=\"pz:rounded-lg pz:bg-popover pz:text-popover-foreground pz:p-2.5 pz:shadow-md pz:ring-1 pz:ring-foreground/10 pz:outline-none pz:w-56\"\n >\n {renderPopover ? (\n renderPopover({\n providers: resolvedProviders,\n startingCostPerProvider: resolvedCosts,\n })\n ) : (\n <DefaultPopoverBody\n providers={resolvedProviders}\n startingCostPerProvider={resolvedCosts}\n />\n )}\n </PreviewCardPrimitive.Popup>\n </PreviewCardPrimitive.Positioner>\n </PreviewCardPrimitive.Portal>\n </PreviewCardPrimitive.Root>\n );\n}\n\nfunction DefaultPopoverBody({\n providers,\n startingCostPerProvider,\n}: {\n providers: readonly ProviderName[];\n startingCostPerProvider?: Partial<Record<ProviderName, number>>;\n}) {\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-1.5\">\n <h4 className=\"pz:m-0 pz:text-sm pz:font-medium\">Providers</h4>\n <ul className=\"pz:flex pz:flex-col pz:gap-1.5 pz:list-none pz:m-0 pz:p-0\">\n {providers.map((provider) => {\n const entry = getProviderEntry(provider);\n const cost = startingCostPerProvider?.[provider];\n return (\n <li key={provider} className=\"pz:flex pz:items-center pz:gap-2\">\n <img\n src={entry?.logoUrl ?? \"/placeholder.svg?height=24&width=24\"}\n alt={entry?.label ?? provider}\n className=\"pz:rounded-md pz:h-6 pz:w-6 pz:object-cover pz:bg-background pz:border pz:border-input\"\n />\n <span className=\"pz:text-sm pz:flex-1 pz:truncate\">{entry?.label ?? provider}</span>\n {startingCostPerProvider ? (\n <span className=\"pz:inline-flex pz:items-center pz:gap-1 pz:text-xs pz:text-muted-foreground\">\n <Coins className=\"pz:size-3\" />\n {cost ?? \"Free\"}\n </span>\n ) : null}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6BA,SAAgB,uBAAuB,EACrC,WACA,yBACA,cACA,eACA,MACA,WACA,WACA,GAAG,QAC2B;CAC9B,MAAM,OAAO,2BAA2B,EAAE;CAC1C,MAAM,kBAAkB,oBAAoB;CAC5C,MAAM,oBACJ,cACC,QAAQ,eAAe,OAAQ,KAAK,YAA2C,WAChF,EAAE;CACJ,MAAM,gBACJ,4BACC,QAAQ,6BAA6B,OAAO,KAAK,0BAA0B;AAE9E,KAAI,CAAC,kBAAkB,OAAQ,QAAO;AAEtC,KAAI,aACF,QACE,oBAAC,aAAD;EACE,WAAW;EACL;EACK;EACA;EACX,UAAU,MAAM,EAAE,iBAAiB;EACnC,GAAI;EACJ;AAIN,QACE,qBAACA,YAAqB,MAAtB,aACE,oBAACA,YAAqB,SAAtB,EACE,SAAS,iBACP,oBAAC,aAAD;EACE,WAAW;EACL;EACK;EACX,WAAW,GAAG,qBAAqB,UAAU;EAC7C,GAAI;EACJ,GAAK;EACL,UAAU,MAAkC;AAC1C,KAAE,iBAAiB;GACnB,MAAM,iBACJ,aAGA;AACF,oBAAiB,EAAE;;EAErB,GAEJ,GACF,oBAACA,YAAqB,QAAtB;EAA6B,WAAW;YACtC,oBAACA,YAAqB,YAAtB;GACE,MAAK;GACL,YAAY;GACZ,WAAU;aAEV,oBAACA,YAAqB,OAAtB;IACE,WAAQ;IACR,WAAU;cAET,gBACC,cAAc;KACZ,WAAW;KACX,yBAAyB;KAC1B,CAAC,GAEF,oBAAC,oBAAD;KACE,WAAW;KACX,yBAAyB;KACzB;IAEuB;GACG;EACN,EACJ;;AAIhC,SAAS,mBAAmB,EAC1B,WACA,2BAIC;AACD,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,MAAD;GAAI,WAAU;aAAmC;GAAc,GAC/D,oBAAC,MAAD;GAAI,WAAU;aACX,UAAU,KAAK,aAAa;IAC3B,MAAM,QAAQ,iBAAiB,SAAS;IACxC,MAAM,OAAO,0BAA0B;AACvC,WACE,qBAAC,MAAD;KAAmB,WAAU;eAA7B;MACE,oBAAC,OAAD;OACE,KAAK,OAAO,WAAW;OACvB,KAAK,OAAO,SAAS;OACrB,WAAU;OACV;MACF,oBAAC,QAAD;OAAM,WAAU;iBAAoC,OAAO,SAAS;OAAgB;MACnF,0BACC,qBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,oBAAC,OAAD,EAAO,WAAU,aAAc,GAC9B,QAAQ,OACJ;WACL;MACD;OAbI,SAaJ;KAEP;GACC,EACD"}
@@ -1,11 +1,11 @@
1
1
  import { cn } from "../../../lib/utils.mjs";
2
+ import { Asterisk } from "../../internal/icons.mjs";
2
3
  import { useFormAlert, useFormConfig } from "../../../context/form-context.mjs";
3
4
  import { HoverInfo } from "../../hover-info.mjs";
4
5
  import { RichText } from "../../rich-text.mjs";
5
6
  import { mergeProps } from "@base-ui/react/merge-props";
6
7
  import { useRender } from "@base-ui/react/use-render";
7
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { Asterisk } from "lucide-react";
9
9
 
10
10
  //#region src/components/defaults/layout/field-wrapper.tsx
11
11
  function DefaultFieldWrapper({ field, children, className, render, ...props }) {
@@ -21,7 +21,7 @@ function DefaultFieldWrapper({ field, children, className, render, ...props }) {
21
21
  children: [field.label && /* @__PURE__ */ jsxs("label", {
22
22
  "data-p0": "label",
23
23
  htmlFor: field.id,
24
- className: classNames?.label ?? cn("pz:text-sm pz:leading-none pz:font-medium pz:select-none", hasError && "pz:text-destructive"),
24
+ className: classNames?.label ?? cn("pz:text-[13px] pz:leading-none pz:font-medium pz:select-none", hasError && "pz:text-destructive"),
25
25
  children: [/* @__PURE__ */ jsx(RichText, { children: field.label }), field.meta.required && /* @__PURE__ */ jsx(Asterisk, {
26
26
  "aria-label": "required",
27
27
  className: "pz:ml-0.5 pz:inline-block pz:size-3 pz:align-top pz:text-primary"
@@ -1 +1 @@
1
- {"version":3,"file":"field-wrapper.mjs","names":[],"sources":["../../../../src/components/defaults/layout/field-wrapper.tsx"],"sourcesContent":["import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Asterisk } from \"lucide-react\";\nimport { useFormAlert, useFormConfig } from \"../../../context/form-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { AnyFieldProps } from \"../../../types/field-props.js\";\nimport { HoverInfo } from \"../../hover-info.js\";\nimport { RichText } from \"../../rich-text.js\";\n\nexport interface DefaultFieldWrapperState {\n field: AnyFieldProps;\n disabled: boolean;\n hasError: boolean;\n}\n\nexport interface DefaultFieldWrapperProps\n extends useRender.ComponentProps<\"div\", DefaultFieldWrapperState> {\n field: AnyFieldProps;\n}\n\nexport function DefaultFieldWrapper({\n field,\n children,\n className,\n render,\n ...props\n}: DefaultFieldWrapperProps) {\n const { classNames } = useFormConfig();\n const Alert = useFormAlert();\n const hasError = !!field.error;\n const isDisabled = field.disabled;\n\n // Boolean toggles render their label inline with the switch (settings-row\n // pattern), so the wrapper's label/description block is suppressed for them.\n const inlineLabel = field.kind === \"boolean_input\";\n\n const defaultContent = (\n <>\n {!inlineLabel && (field.label || field.description || field.info) && (\n <div className=\"pz:flex pz:flex-col pz:gap-1\">\n <div className=\"pz:flex pz:items-center pz:gap-1.5\">\n {field.label && (\n <label\n data-p0=\"label\"\n htmlFor={field.id}\n className={\n classNames?.label ??\n cn(\n \"pz:text-sm pz:leading-none pz:font-medium pz:select-none\",\n hasError && \"pz:text-destructive\",\n )\n }\n >\n <RichText>{field.label}</RichText>\n {field.meta.required && (\n <Asterisk\n aria-label=\"required\"\n className=\"pz:ml-0.5 pz:inline-block pz:size-3 pz:align-top pz:text-primary\"\n />\n )}\n </label>\n )}\n {field.info && (\n <HoverInfo>\n <RichText>{field.info}</RichText>\n </HoverInfo>\n )}\n </div>\n {field.description && (\n <p\n data-p0=\"description\"\n className={classNames?.description ?? \"pz:text-xs pz:text-muted-foreground\"}\n >\n <RichText>{field.description}</RichText>\n </p>\n )}\n </div>\n )}\n {isDisabled ? (\n <div className=\"pz:opacity-60 pz:pointer-events-none\">{children}</div>\n ) : (\n children\n )}\n {isDisabled && field.disabledReason && (\n <Alert variant=\"info\" data-p0=\"disabled-reason\" className={classNames?.disabledReason}>\n <RichText>{field.disabledReason}</RichText>\n </Alert>\n )}\n {hasError && (\n <span\n data-p0=\"error\"\n className={classNames?.error ?? \"pz:text-destructive pz:text-xs pz:font-medium\"}\n role=\"alert\"\n >\n <RichText>{field.error}</RichText>\n </span>\n )}\n </>\n );\n\n return useRender({\n defaultTagName: \"div\",\n render,\n state: { field, disabled: isDisabled, hasError },\n stateAttributesMapping: {\n field: () => null,\n },\n props: mergeProps<\"div\">(\n {\n className: cn(\n classNames?.field ?? \"pz:grid pz:gap-2\",\n isDisabled && classNames?.fieldDisabled,\n className,\n ),\n children: defaultContent,\n ...({\n \"data-p0\": \"field\",\n \"data-p0-field\": field.path,\n \"data-p0-kind\": field.kind,\n ...(isDisabled ? { \"data-p0-disabled\": \"true\", \"aria-disabled\": \"true\" } : {}),\n } as Record<string, string>),\n },\n props,\n ),\n });\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,oBAAoB,EAClC,OACA,UACA,WACA,QACA,GAAG,SACwB;CAC3B,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,QAAQ,cAAc;CAC5B,MAAM,WAAW,CAAC,CAAC,MAAM;CACzB,MAAM,aAAa,MAAM;CAMzB,MAAM,iBACJ;EACG,EAJe,MAAM,SAAS,qBAIb,MAAM,SAAS,MAAM,eAAe,MAAM,SAC1D,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,MAAM,SACL,qBAAC,SAAD;KACE,WAAQ;KACR,SAAS,MAAM;KACf,WACE,YAAY,SACZ,GACE,4DACA,YAAY,sBACb;eARL,CAWE,oBAAC,UAAD,YAAW,MAAM,OAAiB,GACjC,MAAM,KAAK,YACV,oBAAC,UAAD;MACE,cAAW;MACX,WAAU;MACV,EAEE;QAET,MAAM,QACL,oBAAC,WAAD,YACE,oBAAC,UAAD,YAAW,MAAM,MAAgB,GACvB,EAEV;OACL,MAAM,eACL,oBAAC,KAAD;IACE,WAAQ;IACR,WAAW,YAAY,eAAe;cAEtC,oBAAC,UAAD,YAAW,MAAM,aAAuB;IACtC,EAEF;;EAEP,aACC,oBAAC,OAAD;GAAK,WAAU;GAAwC;GAAe,IAEtE;EAED,cAAc,MAAM,kBACnB,oBAAC,OAAD;GAAO,SAAQ;GAAO,WAAQ;GAAkB,WAAW,YAAY;aACrE,oBAAC,UAAD,YAAW,MAAM,gBAA0B;GACrC;EAET,YACC,oBAAC,QAAD;GACE,WAAQ;GACR,WAAW,YAAY,SAAS;GAChC,MAAK;aAEL,oBAAC,UAAD,YAAW,MAAM,OAAiB;GAC7B;EAER;AAGL,QAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GAAE;GAAO,UAAU;GAAY;GAAU;EAChD,wBAAwB,EACtB,aAAa,MACd;EACD,OAAO,WACL;GACE,WAAW,GACT,YAAY,SAAS,oBACrB,cAAc,YAAY,eAC1B,UACD;GACD,UAAU;GAER,WAAW;GACX,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,GAAI,aAAa;IAAE,oBAAoB;IAAQ,iBAAiB;IAAQ,GAAG,EAAE;GAEhF,EACD,MACD;EACF,CAAC"}
1
+ {"version":3,"file":"field-wrapper.mjs","names":[],"sources":["../../../../src/components/defaults/layout/field-wrapper.tsx"],"sourcesContent":["import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useFormAlert, useFormConfig } from \"../../../context/form-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { AnyFieldProps } from \"../../../types/field-props.js\";\nimport { HoverInfo } from \"../../hover-info.js\";\nimport { Asterisk } from \"../../internal/icons.js\";\nimport { RichText } from \"../../rich-text.js\";\n\nexport interface DefaultFieldWrapperState {\n field: AnyFieldProps;\n disabled: boolean;\n hasError: boolean;\n}\n\nexport interface DefaultFieldWrapperProps\n extends useRender.ComponentProps<\"div\", DefaultFieldWrapperState> {\n field: AnyFieldProps;\n}\n\nexport function DefaultFieldWrapper({\n field,\n children,\n className,\n render,\n ...props\n}: DefaultFieldWrapperProps) {\n const { classNames } = useFormConfig();\n const Alert = useFormAlert();\n const hasError = !!field.error;\n const isDisabled = field.disabled;\n\n // Boolean toggles render their label inline with the switch (settings-row\n // pattern), so the wrapper's label/description block is suppressed for them.\n const inlineLabel = field.kind === \"boolean_input\";\n\n const defaultContent = (\n <>\n {!inlineLabel && (field.label || field.description || field.info) && (\n <div className=\"pz:flex pz:flex-col pz:gap-1\">\n <div className=\"pz:flex pz:items-center pz:gap-1.5\">\n {field.label && (\n <label\n data-p0=\"label\"\n htmlFor={field.id}\n className={\n classNames?.label ??\n cn(\n \"pz:text-[13px] pz:leading-none pz:font-medium pz:select-none\",\n hasError && \"pz:text-destructive\",\n )\n }\n >\n <RichText>{field.label}</RichText>\n {field.meta.required && (\n <Asterisk\n aria-label=\"required\"\n className=\"pz:ml-0.5 pz:inline-block pz:size-3 pz:align-top pz:text-primary\"\n />\n )}\n </label>\n )}\n {field.info && (\n <HoverInfo>\n <RichText>{field.info}</RichText>\n </HoverInfo>\n )}\n </div>\n {field.description && (\n <p\n data-p0=\"description\"\n className={classNames?.description ?? \"pz:text-xs pz:text-muted-foreground\"}\n >\n <RichText>{field.description}</RichText>\n </p>\n )}\n </div>\n )}\n {isDisabled ? (\n <div className=\"pz:opacity-60 pz:pointer-events-none\">{children}</div>\n ) : (\n children\n )}\n {isDisabled && field.disabledReason && (\n <Alert variant=\"info\" data-p0=\"disabled-reason\" className={classNames?.disabledReason}>\n <RichText>{field.disabledReason}</RichText>\n </Alert>\n )}\n {hasError && (\n <span\n data-p0=\"error\"\n className={classNames?.error ?? \"pz:text-destructive pz:text-xs pz:font-medium\"}\n role=\"alert\"\n >\n <RichText>{field.error}</RichText>\n </span>\n )}\n </>\n );\n\n return useRender({\n defaultTagName: \"div\",\n render,\n state: { field, disabled: isDisabled, hasError },\n stateAttributesMapping: {\n field: () => null,\n },\n props: mergeProps<\"div\">(\n {\n className: cn(\n classNames?.field ?? \"pz:grid pz:gap-2\",\n isDisabled && classNames?.fieldDisabled,\n className,\n ),\n children: defaultContent,\n ...({\n \"data-p0\": \"field\",\n \"data-p0-field\": field.path,\n \"data-p0-kind\": field.kind,\n ...(isDisabled ? { \"data-p0-disabled\": \"true\", \"aria-disabled\": \"true\" } : {}),\n } as Record<string, string>),\n },\n props,\n ),\n });\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAgB,oBAAoB,EAClC,OACA,UACA,WACA,QACA,GAAG,SACwB;CAC3B,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,QAAQ,cAAc;CAC5B,MAAM,WAAW,CAAC,CAAC,MAAM;CACzB,MAAM,aAAa,MAAM;CAMzB,MAAM,iBACJ;EACG,EAJe,MAAM,SAAS,qBAIb,MAAM,SAAS,MAAM,eAAe,MAAM,SAC1D,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,MAAM,SACL,qBAAC,SAAD;KACE,WAAQ;KACR,SAAS,MAAM;KACf,WACE,YAAY,SACZ,GACE,gEACA,YAAY,sBACb;eARL,CAWE,oBAAC,UAAD,YAAW,MAAM,OAAiB,GACjC,MAAM,KAAK,YACV,oBAAC,UAAD;MACE,cAAW;MACX,WAAU;MACV,EAEE;QAET,MAAM,QACL,oBAAC,WAAD,YACE,oBAAC,UAAD,YAAW,MAAM,MAAgB,GACvB,EAEV;OACL,MAAM,eACL,oBAAC,KAAD;IACE,WAAQ;IACR,WAAW,YAAY,eAAe;cAEtC,oBAAC,UAAD,YAAW,MAAM,aAAuB;IACtC,EAEF;;EAEP,aACC,oBAAC,OAAD;GAAK,WAAU;GAAwC;GAAe,IAEtE;EAED,cAAc,MAAM,kBACnB,oBAAC,OAAD;GAAO,SAAQ;GAAO,WAAQ;GAAkB,WAAW,YAAY;aACrE,oBAAC,UAAD,YAAW,MAAM,gBAA0B;GACrC;EAET,YACC,oBAAC,QAAD;GACE,WAAQ;GACR,WAAW,YAAY,SAAS;GAChC,MAAK;aAEL,oBAAC,UAAD,YAAW,MAAM,OAAiB;GAC7B;EAER;AAGL,QAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GAAE;GAAO,UAAU;GAAY;GAAU;EAChD,wBAAwB,EACtB,aAAa,MACd;EACD,OAAO,WACL;GACE,WAAW,GACT,YAAY,SAAS,oBACrB,cAAc,YAAY,eAC1B,UACD;GACD,UAAU;GAER,WAAW;GACX,iBAAiB,MAAM;GACvB,gBAAgB,MAAM;GACtB,GAAI,aAAa;IAAE,oBAAoB;IAAQ,iBAAiB;IAAQ,GAAG,EAAE;GAEhF,EACD,MACD;EACF,CAAC"}
@@ -49,6 +49,7 @@ function DefaultGroup({ group, children, className, render, ...props }) {
49
49
  countLabel && /* @__PURE__ */ jsx(Badge, {
50
50
  variant: showDirty ? "default" : "secondary",
51
51
  "data-p0": "group-badge",
52
+ className: showDirty ? "pz:h-[18px] pz:rounded-[5px] pz:bg-primary/[0.08] pz:px-1.5 pz:text-[11px] pz:text-primary pz:ring-1 pz:ring-primary/25 pz:ring-inset" : "pz:h-[18px] pz:rounded-[5px] pz:bg-muted pz:px-1.5 pz:text-[11px] pz:text-muted-foreground pz:ring-1 pz:ring-border pz:ring-inset",
52
53
  children: countLabel
53
54
  }),
54
55
  hasErrors && /* @__PURE__ */ jsxs(Badge, {
@@ -95,7 +96,7 @@ function DefaultGroup({ group, children, className, render, ...props }) {
95
96
  fields: () => null
96
97
  },
97
98
  props: mergeProps({
98
- className: cn(classNames?.group ?? cn("pz:flex pz:flex-col pz:bg-card pz:border pz:border-border pz:rounded-lg pz:overflow-hidden", hasErrors && "pz:border-destructive"), className),
99
+ className: cn(classNames?.group ?? cn("pz:flex pz:flex-col pz:bg-card pz:border pz:border-border pz:rounded-xl pz:overflow-hidden", hasErrors && "pz:border-destructive"), className),
99
100
  children: defaultContent,
100
101
  "data-p0": "group",
101
102
  "data-p0-group": group.key,
@@ -1 +1 @@
1
- {"version":3,"file":"group.mjs","names":["AccordionPrimitive"],"sources":["../../../../src/components/defaults/layout/group.tsx"],"sourcesContent":["import { Accordion as AccordionPrimitive } from \"@base-ui/react/accordion\";\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useState } from \"react\";\nimport { useFormConfig } from \"../../../context/form-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { AnyFieldProps } from \"../../../types/field-props.js\";\nimport type { FormGroupHandle } from \"../../../types/form-handle.js\";\nimport { IconGlyph } from \"../../../widgets/icon-glyph.js\";\nimport { HoverInfo } from \"../../hover-info.js\";\nimport { IconChevronDown } from \"../../internal/icons.js\";\nimport { Badge } from \"../../ui/badge.js\";\n\nexport interface DefaultGroupState {\n expanded: boolean;\n setExpanded: (expanded: boolean) => void;\n fields: ReadonlyArray<AnyFieldProps>;\n hasErrors: boolean;\n errorCount: number;\n setCount: number;\n dirtyCount: number;\n}\n\nexport interface DefaultGroupProps extends useRender.ComponentProps<\"div\", DefaultGroupState> {\n group: FormGroupHandle;\n}\n\nconst ITEM_VALUE = \"group\";\n\nexport function DefaultGroup({ group, children, className, render, ...props }: DefaultGroupProps) {\n const { classNames } = useFormConfig();\n // Track open state alongside the accordion so consumers reading\n // `state.expanded` (via render-prop) still get the right value.\n const [expanded, setExpanded] = useState(group.defaultExpand);\n const hasErrors = group.errorCount > 0;\n const showDirty = group.dirtyCount > 0;\n const countLabel = showDirty\n ? `${group.dirtyCount} modified`\n : group.setCount > 0\n ? `${group.setCount} set`\n : null;\n\n const defaultContent = (\n <AccordionPrimitive.Root\n value={expanded ? [ITEM_VALUE] : []}\n onValueChange={(next) => setExpanded(next.includes(ITEM_VALUE))}\n >\n <AccordionPrimitive.Item value={ITEM_VALUE}>\n <AccordionPrimitive.Header className=\"pz:flex\">\n <AccordionPrimitive.Trigger\n data-p0=\"group-header\"\n className={\n classNames?.groupHeader ??\n cn(\n \"pz:group/group-trigger\",\n \"pz:flex pz:items-center pz:justify-between pz:gap-4 pz:w-full\",\n \"pz:px-3.5 pz:py-2.5 pz:cursor-pointer pz:text-sm pz:font-medium pz:text-left\",\n \"pz:transition-colors pz:hover:bg-muted/60\",\n \"pz:aria-expanded:hover:bg-transparent\",\n \"pz:focus-visible:outline-none\",\n hasErrors && \"pz:text-destructive pz:bg-destructive/10\",\n )\n }\n >\n <span className=\"pz:flex pz:items-center pz:gap-2\">\n {group.iconKey && (\n <IconGlyph\n keyName={group.iconKey}\n size={14}\n className=\"pz:text-muted-foreground pz:shrink-0\"\n />\n )}\n {group.label ?? group.key}\n {group.description && (\n <span onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>\n <HoverInfo>{group.description}</HoverInfo>\n </span>\n )}\n </span>\n <span className=\"pz:flex pz:items-center pz:gap-2\">\n {countLabel && (\n <Badge variant={showDirty ? \"default\" : \"secondary\"} data-p0=\"group-badge\">\n {countLabel}\n </Badge>\n )}\n {hasErrors && (\n <Badge variant=\"destructive\" data-p0=\"group-error-badge\">\n {group.errorCount} {group.errorCount === 1 ? \"error\" : \"errors\"}\n </Badge>\n )}\n <IconChevronDown\n width={16}\n height={16}\n className={cn(\n \"pz:text-muted-foreground pz:transition-transform pz:duration-150\",\n \"pz:group-aria-expanded/group-trigger:rotate-180\",\n )}\n />\n </span>\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n <AccordionPrimitive.Panel\n data-p0=\"group-content\"\n // The library ships keyframes via `tw-animate-css` —\n // `animate-accordion-down` / `animate-accordion-up` plus the\n // `--accordion-panel-height` var that base-ui populates on the\n // panel give a smooth height transition without JS measuring.\n className=\"pz:overflow-hidden pz:border-t pz:border-border pz:data-open:animate-accordion-down pz:data-closed:animate-accordion-up\"\n >\n <div\n className={\n classNames?.groupContent ??\n cn(\n \"pz:flex pz:flex-col pz:gap-3 pz:px-3.5 pz:pb-2 pz:pt-2\",\n \"pz:h-(--accordion-panel-height) pz:data-ending-style:h-0 pz:data-starting-style:h-0\",\n )\n }\n >\n {children}\n </div>\n </AccordionPrimitive.Panel>\n </AccordionPrimitive.Item>\n </AccordionPrimitive.Root>\n );\n\n return useRender({\n defaultTagName: \"div\",\n render,\n state: {\n expanded,\n setExpanded,\n fields: group.fields,\n hasErrors,\n errorCount: group.errorCount,\n setCount: group.setCount,\n dirtyCount: group.dirtyCount,\n },\n stateAttributesMapping: {\n setExpanded: () => null,\n fields: () => null,\n },\n props: mergeProps<\"div\">(\n {\n className: cn(\n classNames?.group ??\n cn(\n \"pz:flex pz:flex-col pz:bg-card pz:border pz:border-border pz:rounded-lg pz:overflow-hidden\",\n hasErrors && \"pz:border-destructive\",\n ),\n className,\n ),\n children: defaultContent,\n ...({\n \"data-p0\": \"group\",\n \"data-p0-group\": group.key,\n \"data-p0-expanded\": String(expanded),\n } as Record<string, string>),\n },\n props,\n ),\n });\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,aAAa;AAEnB,SAAgB,aAAa,EAAE,OAAO,UAAU,WAAW,QAAQ,GAAG,SAA4B;CAChG,MAAM,EAAE,eAAe,eAAe;CAGtC,MAAM,CAAC,UAAU,eAAe,SAAS,MAAM,cAAc;CAC7D,MAAM,YAAY,MAAM,aAAa;CACrC,MAAM,YAAY,MAAM,aAAa;CACrC,MAAM,aAAa,YACf,GAAG,MAAM,WAAW,aACpB,MAAM,WAAW,IACf,GAAG,MAAM,SAAS,QAClB;CAEN,MAAM,iBACJ,oBAACA,UAAmB,MAApB;EACE,OAAO,WAAW,CAAC,WAAW,GAAG,EAAE;EACnC,gBAAgB,SAAS,YAAY,KAAK,SAAS,WAAW,CAAC;YAE/D,qBAACA,UAAmB,MAApB;GAAyB,OAAO;aAAhC,CACE,oBAACA,UAAmB,QAApB;IAA2B,WAAU;cACnC,qBAACA,UAAmB,SAApB;KACE,WAAQ;KACR,WACE,YAAY,eACZ,GACE,0BACA,iEACA,gFACA,6CACA,yCACA,iCACA,aAAa,2CACd;eAZL,CAeE,qBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,MAAM,WACL,oBAAC,WAAD;QACE,SAAS,MAAM;QACf,MAAM;QACN,WAAU;QACV;OAEH,MAAM,SAAS,MAAM;OACrB,MAAM,eACL,oBAAC,QAAD;QAAM,UAAU,MAAM,EAAE,iBAAiB;QAAE,YAAY,MAAM,EAAE,iBAAiB;kBAC9E,oBAAC,WAAD,YAAY,MAAM,aAAwB;QACrC;OAEJ;SACP,qBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,cACC,oBAAC,OAAD;QAAO,SAAS,YAAY,YAAY;QAAa,WAAQ;kBAC1D;QACK;OAET,aACC,qBAAC,OAAD;QAAO,SAAQ;QAAc,WAAQ;kBAArC;SACG,MAAM;SAAW;SAAE,MAAM,eAAe,IAAI,UAAU;SACjD;;OAEV,oBAAC,iBAAD;QACE,OAAO;QACP,QAAQ;QACR,WAAW,GACT,oEACA,kDACD;QACD;OACG;QACoB;;IACH,GAC5B,oBAACA,UAAmB,OAApB;IACE,WAAQ;IAKR,WAAU;cAEV,oBAAC,OAAD;KACE,WACE,YAAY,gBACZ,GACE,0DACA,sFACD;KAGF;KACG;IACmB,EACH;;EACF;AAG5B,QAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GACL;GACA;GACA,QAAQ,MAAM;GACd;GACA,YAAY,MAAM;GAClB,UAAU,MAAM;GAChB,YAAY,MAAM;GACnB;EACD,wBAAwB;GACtB,mBAAmB;GACnB,cAAc;GACf;EACD,OAAO,WACL;GACE,WAAW,GACT,YAAY,SACV,GACE,8FACA,aAAa,wBACd,EACH,UACD;GACD,UAAU;GAER,WAAW;GACX,iBAAiB,MAAM;GACvB,oBAAoB,OAAO,SAAS;GAEvC,EACD,MACD;EACF,CAAC"}
1
+ {"version":3,"file":"group.mjs","names":["AccordionPrimitive"],"sources":["../../../../src/components/defaults/layout/group.tsx"],"sourcesContent":["import { Accordion as AccordionPrimitive } from \"@base-ui/react/accordion\";\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useState } from \"react\";\nimport { useFormConfig } from \"../../../context/form-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { AnyFieldProps } from \"../../../types/field-props.js\";\nimport type { FormGroupHandle } from \"../../../types/form-handle.js\";\nimport { IconGlyph } from \"../../../widgets/icon-glyph.js\";\nimport { HoverInfo } from \"../../hover-info.js\";\nimport { IconChevronDown } from \"../../internal/icons.js\";\nimport { Badge } from \"../../ui/badge.js\";\n\nexport interface DefaultGroupState {\n expanded: boolean;\n setExpanded: (expanded: boolean) => void;\n fields: ReadonlyArray<AnyFieldProps>;\n hasErrors: boolean;\n errorCount: number;\n setCount: number;\n dirtyCount: number;\n}\n\nexport interface DefaultGroupProps extends useRender.ComponentProps<\"div\", DefaultGroupState> {\n group: FormGroupHandle;\n}\n\nconst ITEM_VALUE = \"group\";\n\nexport function DefaultGroup({ group, children, className, render, ...props }: DefaultGroupProps) {\n const { classNames } = useFormConfig();\n // Track open state alongside the accordion so consumers reading\n // `state.expanded` (via render-prop) still get the right value.\n const [expanded, setExpanded] = useState(group.defaultExpand);\n const hasErrors = group.errorCount > 0;\n const showDirty = group.dirtyCount > 0;\n const countLabel = showDirty\n ? `${group.dirtyCount} modified`\n : group.setCount > 0\n ? `${group.setCount} set`\n : null;\n\n const defaultContent = (\n <AccordionPrimitive.Root\n value={expanded ? [ITEM_VALUE] : []}\n onValueChange={(next) => setExpanded(next.includes(ITEM_VALUE))}\n >\n <AccordionPrimitive.Item value={ITEM_VALUE}>\n <AccordionPrimitive.Header className=\"pz:flex\">\n <AccordionPrimitive.Trigger\n data-p0=\"group-header\"\n className={\n classNames?.groupHeader ??\n cn(\n \"pz:group/group-trigger\",\n \"pz:flex pz:items-center pz:justify-between pz:gap-4 pz:w-full\",\n \"pz:px-3.5 pz:py-2.5 pz:cursor-pointer pz:text-sm pz:font-medium pz:text-left\",\n \"pz:transition-colors pz:hover:bg-muted/60\",\n \"pz:aria-expanded:hover:bg-transparent\",\n \"pz:focus-visible:outline-none\",\n hasErrors && \"pz:text-destructive pz:bg-destructive/10\",\n )\n }\n >\n <span className=\"pz:flex pz:items-center pz:gap-2\">\n {group.iconKey && (\n <IconGlyph\n keyName={group.iconKey}\n size={14}\n className=\"pz:text-muted-foreground pz:shrink-0\"\n />\n )}\n {group.label ?? group.key}\n {group.description && (\n <span onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>\n <HoverInfo>{group.description}</HoverInfo>\n </span>\n )}\n </span>\n <span className=\"pz:flex pz:items-center pz:gap-2\">\n {countLabel && (\n <Badge\n variant={showDirty ? \"default\" : \"secondary\"}\n data-p0=\"group-badge\"\n // One boxed chip for both states; \"modified\" only shifts to a\n // primary tint, so it reads as a change, not an alarm.\n className={\n showDirty\n ? \"pz:h-[18px] pz:rounded-[5px] pz:bg-primary/[0.08] pz:px-1.5 pz:text-[11px] pz:text-primary pz:ring-1 pz:ring-primary/25 pz:ring-inset\"\n : \"pz:h-[18px] pz:rounded-[5px] pz:bg-muted pz:px-1.5 pz:text-[11px] pz:text-muted-foreground pz:ring-1 pz:ring-border pz:ring-inset\"\n }\n >\n {countLabel}\n </Badge>\n )}\n {hasErrors && (\n <Badge variant=\"destructive\" data-p0=\"group-error-badge\">\n {group.errorCount} {group.errorCount === 1 ? \"error\" : \"errors\"}\n </Badge>\n )}\n <IconChevronDown\n width={16}\n height={16}\n className={cn(\n \"pz:text-muted-foreground pz:transition-transform pz:duration-150\",\n \"pz:group-aria-expanded/group-trigger:rotate-180\",\n )}\n />\n </span>\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n <AccordionPrimitive.Panel\n data-p0=\"group-content\"\n // The library ships keyframes via `tw-animate-css` —\n // `animate-accordion-down` / `animate-accordion-up` plus the\n // `--accordion-panel-height` var that base-ui populates on the\n // panel give a smooth height transition without JS measuring.\n className=\"pz:overflow-hidden pz:border-t pz:border-border pz:data-open:animate-accordion-down pz:data-closed:animate-accordion-up\"\n >\n <div\n className={\n classNames?.groupContent ??\n cn(\n \"pz:flex pz:flex-col pz:gap-3 pz:px-3.5 pz:pb-2 pz:pt-2\",\n \"pz:h-(--accordion-panel-height) pz:data-ending-style:h-0 pz:data-starting-style:h-0\",\n )\n }\n >\n {children}\n </div>\n </AccordionPrimitive.Panel>\n </AccordionPrimitive.Item>\n </AccordionPrimitive.Root>\n );\n\n return useRender({\n defaultTagName: \"div\",\n render,\n state: {\n expanded,\n setExpanded,\n fields: group.fields,\n hasErrors,\n errorCount: group.errorCount,\n setCount: group.setCount,\n dirtyCount: group.dirtyCount,\n },\n stateAttributesMapping: {\n setExpanded: () => null,\n fields: () => null,\n },\n props: mergeProps<\"div\">(\n {\n className: cn(\n classNames?.group ??\n cn(\n \"pz:flex pz:flex-col pz:bg-card pz:border pz:border-border pz:rounded-xl pz:overflow-hidden\",\n hasErrors && \"pz:border-destructive\",\n ),\n className,\n ),\n children: defaultContent,\n ...({\n \"data-p0\": \"group\",\n \"data-p0-group\": group.key,\n \"data-p0-expanded\": String(expanded),\n } as Record<string, string>),\n },\n props,\n ),\n });\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,aAAa;AAEnB,SAAgB,aAAa,EAAE,OAAO,UAAU,WAAW,QAAQ,GAAG,SAA4B;CAChG,MAAM,EAAE,eAAe,eAAe;CAGtC,MAAM,CAAC,UAAU,eAAe,SAAS,MAAM,cAAc;CAC7D,MAAM,YAAY,MAAM,aAAa;CACrC,MAAM,YAAY,MAAM,aAAa;CACrC,MAAM,aAAa,YACf,GAAG,MAAM,WAAW,aACpB,MAAM,WAAW,IACf,GAAG,MAAM,SAAS,QAClB;CAEN,MAAM,iBACJ,oBAACA,UAAmB,MAApB;EACE,OAAO,WAAW,CAAC,WAAW,GAAG,EAAE;EACnC,gBAAgB,SAAS,YAAY,KAAK,SAAS,WAAW,CAAC;YAE/D,qBAACA,UAAmB,MAApB;GAAyB,OAAO;aAAhC,CACE,oBAACA,UAAmB,QAApB;IAA2B,WAAU;cACnC,qBAACA,UAAmB,SAApB;KACE,WAAQ;KACR,WACE,YAAY,eACZ,GACE,0BACA,iEACA,gFACA,6CACA,yCACA,iCACA,aAAa,2CACd;eAZL,CAeE,qBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,MAAM,WACL,oBAAC,WAAD;QACE,SAAS,MAAM;QACf,MAAM;QACN,WAAU;QACV;OAEH,MAAM,SAAS,MAAM;OACrB,MAAM,eACL,oBAAC,QAAD;QAAM,UAAU,MAAM,EAAE,iBAAiB;QAAE,YAAY,MAAM,EAAE,iBAAiB;kBAC9E,oBAAC,WAAD,YAAY,MAAM,aAAwB;QACrC;OAEJ;SACP,qBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,cACC,oBAAC,OAAD;QACE,SAAS,YAAY,YAAY;QACjC,WAAQ;QAGR,WACE,YACI,0IACA;kBAGL;QACK;OAET,aACC,qBAAC,OAAD;QAAO,SAAQ;QAAc,WAAQ;kBAArC;SACG,MAAM;SAAW;SAAE,MAAM,eAAe,IAAI,UAAU;SACjD;;OAEV,oBAAC,iBAAD;QACE,OAAO;QACP,QAAQ;QACR,WAAW,GACT,oEACA,kDACD;QACD;OACG;QACoB;;IACH,GAC5B,oBAACA,UAAmB,OAApB;IACE,WAAQ;IAKR,WAAU;cAEV,oBAAC,OAAD;KACE,WACE,YAAY,gBACZ,GACE,0DACA,sFACD;KAGF;KACG;IACmB,EACH;;EACF;AAG5B,QAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GACL;GACA;GACA,QAAQ,MAAM;GACd;GACA,YAAY,MAAM;GAClB,UAAU,MAAM;GAChB,YAAY,MAAM;GACnB;EACD,wBAAwB;GACtB,mBAAmB;GACnB,cAAc;GACf;EACD,OAAO,WACL;GACE,WAAW,GACT,YAAY,SACV,GACE,8FACA,aAAa,wBACd,EACH,UACD;GACD,UAAU;GAER,WAAW;GACX,iBAAiB,MAAM;GACvB,oBAAoB,OAAO,SAAS;GAEvC,EACD,MACD;EACF,CAAC"}
@@ -8,16 +8,13 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
8
  function DefaultSection({ section, children, className, render, hideLabel, ...props }) {
9
9
  const { classNames } = useFormConfig();
10
10
  const hasErrors = section.groups.some((g) => g.errorCount > 0);
11
- const defaultContent = /* @__PURE__ */ jsxs(Fragment, { children: [!hideLabel && section.label && /* @__PURE__ */ jsxs("div", {
11
+ const defaultContent = /* @__PURE__ */ jsxs(Fragment, { children: [!hideLabel && section.label && /* @__PURE__ */ jsx("div", {
12
12
  "data-p0": "section-header",
13
- className: classNames?.sectionLabel ?? "pz:flex pz:items-center pz:gap-2.5 pz:px-1 pz:mb-0.5 pz:mt-2 pz:select-none",
14
- children: [/* @__PURE__ */ jsx("h2", {
15
- className: cn("pz:text-[10.5px] pz:font-medium pz:tracking-[0.12em] pz:uppercase pz:text-muted-foreground pz:m-0 pz:whitespace-nowrap"),
13
+ className: classNames?.sectionLabel ?? "pz:flex pz:items-center pz:px-0.5 pz:mt-3 pz:select-none",
14
+ children: /* @__PURE__ */ jsx("h2", {
15
+ className: cn("pz:text-[13px] pz:font-semibold pz:tracking-[-0.006em] pz:text-foreground pz:m-0 pz:whitespace-nowrap"),
16
16
  children: section.label
17
- }), /* @__PURE__ */ jsx("span", {
18
- "aria-hidden": true,
19
- className: "pz:flex-1 pz:h-px pz:bg-border"
20
- })]
17
+ })
21
18
  }), children] });
22
19
  return useRender({
23
20
  defaultTagName: "div",
@@ -1 +1 @@
1
- {"version":3,"file":"section.mjs","names":[],"sources":["../../../../src/components/defaults/layout/section.tsx"],"sourcesContent":["import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useFormConfig } from \"../../../context/form-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { FormGroupHandle, FormSectionHandle } from \"../../../types/form-handle.js\";\n\nexport interface DefaultSectionState {\n groups: ReadonlyArray<FormGroupHandle>;\n hasErrors: boolean;\n}\n\nexport interface DefaultSectionProps extends useRender.ComponentProps<\"div\", DefaultSectionState> {\n section: FormSectionHandle;\n /**\n * Suppress the section's own header. Set when several consecutive sections\n * share an umbrella label so the label only renders above the first\n * section in the cluster (the renderer in `pipe-form/content.tsx` and\n * `search-form/content.tsx` handles this automatically).\n */\n hideLabel?: boolean;\n}\n\nexport function DefaultSection({\n section,\n children,\n className,\n render,\n hideLabel,\n ...props\n}: DefaultSectionProps) {\n const { classNames } = useFormConfig();\n const hasErrors = section.groups.some((g) => g.errorCount > 0);\n\n // Section descriptions are intentionally not rendered — the umbrella label\n // alone is enough signal, and per-section descriptions add noise once\n // multiple sections sit under one umbrella. Field-level descriptions cover\n // the cases where copy is actually load-bearing.\n const defaultContent = (\n <>\n {!hideLabel && section.label && (\n <div\n data-p0=\"section-header\"\n className={\n classNames?.sectionLabel ??\n \"pz:flex pz:items-center pz:gap-2.5 pz:px-1 pz:mb-0.5 pz:mt-2 pz:select-none\"\n }\n >\n <h2\n className={cn(\n \"pz:text-[10.5px] pz:font-medium pz:tracking-[0.12em] pz:uppercase pz:text-muted-foreground pz:m-0 pz:whitespace-nowrap\",\n )}\n >\n {section.label}\n </h2>\n <span aria-hidden className=\"pz:flex-1 pz:h-px pz:bg-border\" />\n </div>\n )}\n {children}\n </>\n );\n\n return useRender({\n defaultTagName: \"div\",\n render,\n state: { groups: section.groups, hasErrors },\n stateAttributesMapping: {\n groups: () => null,\n },\n props: mergeProps<\"div\">(\n {\n className: cn(classNames?.section ?? \"pz:flex pz:flex-col pz:gap-2\", className),\n children: defaultContent,\n ...({\n \"data-p0\": \"section\",\n \"data-p0-section\": section.key,\n } as Record<string, string>),\n },\n props,\n ),\n });\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,eAAe,EAC7B,SACA,UACA,WACA,QACA,WACA,GAAG,SACmB;CACtB,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,YAAY,QAAQ,OAAO,MAAM,MAAM,EAAE,aAAa,EAAE;CAM9D,MAAM,iBACJ,4CACG,CAAC,aAAa,QAAQ,SACrB,qBAAC,OAAD;EACE,WAAQ;EACR,WACE,YAAY,gBACZ;YAJJ,CAOE,oBAAC,MAAD;GACE,WAAW,GACT,yHACD;aAEA,QAAQ;GACN,GACL,oBAAC,QAAD;GAAM;GAAY,WAAU;GAAmC,EAC3D;KAEP,SACA;AAGL,QAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GAAE,QAAQ,QAAQ;GAAQ;GAAW;EAC5C,wBAAwB,EACtB,cAAc,MACf;EACD,OAAO,WACL;GACE,WAAW,GAAG,YAAY,WAAW,gCAAgC,UAAU;GAC/E,UAAU;GAER,WAAW;GACX,mBAAmB,QAAQ;GAE9B,EACD,MACD;EACF,CAAC"}
1
+ {"version":3,"file":"section.mjs","names":[],"sources":["../../../../src/components/defaults/layout/section.tsx"],"sourcesContent":["import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useFormConfig } from \"../../../context/form-context.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { FormGroupHandle, FormSectionHandle } from \"../../../types/form-handle.js\";\n\nexport interface DefaultSectionState {\n groups: ReadonlyArray<FormGroupHandle>;\n hasErrors: boolean;\n}\n\nexport interface DefaultSectionProps extends useRender.ComponentProps<\"div\", DefaultSectionState> {\n section: FormSectionHandle;\n /**\n * Suppress the section's own header. Set when several consecutive sections\n * share an umbrella label so the label only renders above the first\n * section in the cluster (the renderer in `pipe-form/content.tsx` and\n * `search-form/content.tsx` handles this automatically).\n */\n hideLabel?: boolean;\n}\n\nexport function DefaultSection({\n section,\n children,\n className,\n render,\n hideLabel,\n ...props\n}: DefaultSectionProps) {\n const { classNames } = useFormConfig();\n const hasErrors = section.groups.some((g) => g.errorCount > 0);\n\n // Section descriptions are intentionally not rendered — the umbrella label\n // alone is enough signal, and per-section descriptions add noise once\n // multiple sections sit under one umbrella. Field-level descriptions cover\n // the cases where copy is actually load-bearing.\n const defaultContent = (\n <>\n {!hideLabel && section.label && (\n <div\n data-p0=\"section-header\"\n className={\n classNames?.sectionLabel ?? \"pz:flex pz:items-center pz:px-0.5 pz:mt-3 pz:select-none\"\n }\n >\n <h2\n className={cn(\n // Sentence case and semibold: a heading you read, not a tag.\n // Space above it separates sections — no trailing rule.\n \"pz:text-[13px] pz:font-semibold pz:tracking-[-0.006em] pz:text-foreground pz:m-0 pz:whitespace-nowrap\",\n )}\n >\n {section.label}\n </h2>\n </div>\n )}\n {children}\n </>\n );\n\n return useRender({\n defaultTagName: \"div\",\n render,\n state: { groups: section.groups, hasErrors },\n stateAttributesMapping: {\n groups: () => null,\n },\n props: mergeProps<\"div\">(\n {\n className: cn(classNames?.section ?? \"pz:flex pz:flex-col pz:gap-2\", className),\n children: defaultContent,\n ...({\n \"data-p0\": \"section\",\n \"data-p0-section\": section.key,\n } as Record<string, string>),\n },\n props,\n ),\n });\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,eAAe,EAC7B,SACA,UACA,WACA,QACA,WACA,GAAG,SACmB;CACtB,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,YAAY,QAAQ,OAAO,MAAM,MAAM,EAAE,aAAa,EAAE;CAM9D,MAAM,iBACJ,4CACG,CAAC,aAAa,QAAQ,SACrB,oBAAC,OAAD;EACE,WAAQ;EACR,WACE,YAAY,gBAAgB;YAG9B,oBAAC,MAAD;GACE,WAAW,GAGT,wGACD;aAEA,QAAQ;GACN;EACD,GAEP,SACA;AAGL,QAAO,UAAU;EACf,gBAAgB;EAChB;EACA,OAAO;GAAE,QAAQ,QAAQ;GAAQ;GAAW;EAC5C,wBAAwB,EACtB,cAAc,MACf;EACD,OAAO,WACL;GACE,WAAW,GAAG,YAAY,WAAW,gCAAgC,UAAU;GAC/E,UAAU;GAER,WAAW;GACX,mBAAmB,QAAQ;GAE9B,EACD,MACD;EACF,CAAC"}
@@ -1,11 +1,11 @@
1
1
  import { Button } from "../../ui/button.mjs";
2
+ import { Trash2 } from "../icons.mjs";
2
3
  import { Input } from "../../ui/input.mjs";
3
4
  import { Switch } from "../../ui/switch.mjs";
4
5
  import { useFloatingElement } from "../../../hooks/use-floating-element.mjs";
5
6
  import { useEffect, useId, useMemo, useRef, useState } from "react";
6
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
8
  import { TYPES_TO_FORMATS } from "@pipe0/base";
8
- import { Trash2 } from "lucide-react";
9
9
  import { offset, shift } from "@floating-ui/react";
10
10
 
11
11
  //#region src/components/internal/LiquidEditor/ChipEditPopover.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"ChipEditPopover.mjs","names":[],"sources":["../../../../src/components/internal/LiquidEditor/ChipEditPopover.tsx"],"sourcesContent":["import { offset, shift } from \"@floating-ui/react\";\nimport { TYPES_TO_FORMATS } from \"@pipe0/base\";\nimport type { Editor } from \"@tiptap/react\";\nimport { Trash2 } from \"lucide-react\";\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { useFloatingElement } from \"../../../hooks/use-floating-element.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Input } from \"../../ui/input.js\";\nimport { Switch } from \"../../ui/switch.js\";\nimport type { ChipKind } from \"../tag-chip-decoration.js\";\n\nconst SIMPLE_FIELD_RE = /^\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_.]*)\\s*(\\|\\s*default:\\s*\"\")?\\s*\\}\\}$/;\nconst SECRET_RE = /^\\{\\{\\s*SECRETS\\.([A-Z][A-Z0-9_]*)\\s*\\}\\}$/;\nconst CONSTANT_RE = /^\\{\\{\\s*CONSTANTS\\.([A-Z][A-Z0-9_]*)\\s*\\}\\}$/;\n// `{% output NAME, key: \"value\", key: true, ... %}` in any argument order. The\n// args are split out by OUTPUT_ARG_RE; BlockChipBody only edits the keys it\n// knows and falls back to the raw \"advanced\" view for anything else.\nconst OUTPUT_BLOCK_RE =\n /^\\{%\\s*output\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*((?:,\\s*[a-z_]+:\\s*(?:\"(?:[^\"\\\\]|\\\\.)*\"|true|false)\\s*)*)%\\}$/;\nconst OUTPUT_ARG_RE = /([a-z_]+):\\s*(\"(?:[^\"\\\\]|\\\\.)*\"|true|false)/g;\nconst OUTPUT_KEYS = new Set([\"type\", \"schema\", \"description\", \"format\", \"required\"]);\n\ntype OutputType = \"string\" | \"number\" | \"boolean\" | \"json\" | \"unknown\";\n\nfunction parseOutputBlock(raw: string) {\n const m = raw.match(OUTPUT_BLOCK_RE);\n if (!m) return null;\n const args: Record<string, string | boolean> = {};\n for (const [, key, value] of (m[2] ?? \"\").matchAll(OUTPUT_ARG_RE)) {\n if (!key || !value || !OUTPUT_KEYS.has(key)) return null;\n args[key] =\n value === \"true\" || value === \"false\"\n ? value === \"true\"\n : value.slice(1, -1).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n const type = args.type;\n if (typeof type !== \"string\" || !(type in TYPES_TO_FORMATS)) return null;\n // A hand-written format the type does not take has no option to show; keep\n // the raw view rather than silently dropping it on save.\n const format = args.format;\n if (\n typeof format === \"string\" &&\n type !== \"json\" &&\n !(TYPES_TO_FORMATS[type as OutputType] as readonly string[]).includes(format)\n ) {\n return null;\n }\n return {\n name: m[1] ?? \"\",\n type: type as OutputType,\n schema: typeof args.schema === \"string\" ? args.schema : \"\",\n description: typeof args.description === \"string\" ? args.description : \"\",\n format: typeof args.format === \"string\" ? args.format : \"\",\n required: typeof args.required === \"boolean\" ? args.required : undefined,\n };\n}\n\nexport interface ChipEditTarget {\n kind: ChipKind;\n raw: string;\n from: number;\n to: number;\n rect: DOMRect;\n}\n\n/**\n * Contextual popover anchored to a chip's DOM rect. Different content per\n * chip kind:\n * - field: switch for `| default: \"\"` (simple cases only), remove.\n * - secret: publicId header, manage/remove buttons. Power users still\n * edit by hand.\n * - block (output declaration): name / type / format / description editors.\n *\n * On any mutation, the popover dispatches a single replaceWith transaction\n * over the chip's range, then closes itself. The Liquid serialization\n * stays identical to what would have been authored by hand.\n */\nexport function ChipEditPopover({\n editor,\n target,\n onClose,\n}: {\n editor: Editor;\n target: ChipEditTarget;\n onClose: () => void;\n}) {\n const { ref, style, isMounted, getFloatingProps } = useFloatingElement(true, target.rect, 1100, {\n placement: \"bottom-start\",\n middleware: [offset(6), shift()],\n onOpenChange(open) {\n if (!open) onClose();\n },\n dismissOptions: { outsidePress: true, outsidePressEvent: \"mousedown\" },\n });\n\n const popupRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") onClose();\n }\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [onClose]);\n\n function replaceWith(next: string) {\n editor.chain().focus().insertContentAt({ from: target.from, to: target.to }, next).run();\n onClose();\n }\n\n function remove() {\n editor.chain().focus().deleteRange({ from: target.from, to: target.to }).run();\n onClose();\n }\n\n if (!isMounted) return null;\n\n return (\n <div\n ref={(el) => {\n popupRef.current = el;\n ref(el);\n }}\n style={style}\n {...getFloatingProps()}\n onMouseDown={(e) => e.stopPropagation()}\n className=\"pz:flex pz:flex-col pz:gap-2 pz:rounded-md pz:border pz:border-input pz:bg-popover pz:p-3 pz:text-popover-foreground pz:shadow-md pz:min-w-72\"\n role=\"dialog\"\n aria-label={`Edit ${target.kind} chip`}\n >\n {target.kind === \"field\" && (\n <FieldChipBody raw={target.raw} replaceWith={replaceWith} remove={remove} />\n )}\n {target.kind === \"secret\" && <SecretChipBody raw={target.raw} remove={remove} />}\n {target.kind === \"constant\" && <ConstantChipBody raw={target.raw} remove={remove} />}\n {target.kind === \"block\" && (\n <BlockChipBody raw={target.raw} replaceWith={replaceWith} remove={remove} />\n )}\n </div>\n );\n}\n\nfunction FieldChipBody({\n raw,\n replaceWith,\n remove,\n}: {\n raw: string;\n replaceWith: (next: string) => void;\n remove: () => void;\n}) {\n const match = raw.match(SIMPLE_FIELD_RE);\n const name = match?.[1];\n const allowEmpty = !!match?.[2];\n const isSimple = !!match;\n\n if (!isSimple || !name) {\n return (\n <>\n <header className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Field reference (advanced)\n </header>\n <p className=\"pz:text-xs pz:text-muted-foreground\">\n This chip uses a custom Liquid filter chain. Edit the raw text in the editor to change it.\n </p>\n <pre className=\"pz:overflow-x-auto pz:rounded-sm pz:bg-muted pz:px-2 pz:py-1 pz:text-xs\">\n {raw}\n </pre>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n }\n\n function toggleAllowEmpty() {\n replaceWith(allowEmpty ? `{{ ${name} }}` : `{{ ${name} | default: \"\" }}`);\n }\n\n return (\n <>\n <header className=\"pz:flex pz:items-center pz:justify-between pz:gap-2\">\n <span className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">Field reference</span>\n <code className=\"pz:text-xs pz:text-sky-700\">{name}</code>\n </header>\n <div className=\"pz:flex pz:items-center pz:justify-between pz:gap-3 pz:text-sm\">\n <span className=\"pz:flex pz:flex-col\">\n <span>Allow empty</span>\n <span className=\"pz:text-xs pz:text-muted-foreground\">\n Inserts <code>| default: \"\"</code> so missing values render as empty.\n </span>\n </span>\n <Switch checked={allowEmpty} onCheckedChange={toggleAllowEmpty} />\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n}\n\nfunction SecretChipBody({ raw, remove }: { raw: string; remove: () => void }) {\n const publicId = raw.match(SECRET_RE)?.[1];\n\n return (\n <>\n <header className=\"pz:flex pz:items-center pz:justify-between pz:gap-2\">\n <span className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">Secret reference</span>\n <code className=\"pz:text-xs pz:text-amber-700\">{publicId ?? \"(unknown)\"}</code>\n </header>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n}\n\nfunction ConstantChipBody({ raw, remove }: { raw: string; remove: () => void }) {\n const publicId = raw.match(CONSTANT_RE)?.[1];\n\n return (\n <>\n <header className=\"pz:flex pz:items-center pz:justify-between pz:gap-2\">\n <span className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Constant reference\n </span>\n <code className=\"pz:text-xs pz:text-emerald-700\">{publicId ?? \"(unknown)\"}</code>\n </header>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n}\n\nfunction BlockChipBody({\n raw,\n replaceWith,\n remove,\n}: {\n raw: string;\n replaceWith: (next: string) => void;\n remove: () => void;\n}) {\n const initial = useMemo(() => parseOutputBlock(raw), [raw]);\n\n const [name, setName] = useState(initial?.name ?? \"\");\n const [type, setType] = useState<OutputType>(initial?.type ?? \"string\");\n const [schema, setSchema] = useState(initial?.schema ?? \"\");\n const [description, setDescription] = useState(initial?.description ?? \"\");\n const [format, setFormat] = useState(initial?.format ?? \"\");\n const formatOptions: readonly string[] = TYPES_TO_FORMATS[type];\n const idPrefix = useId();\n const nameId = `${idPrefix}-name`;\n const typeId = `${idPrefix}-type`;\n const schemaId = `${idPrefix}-schema`;\n const descId = `${idPrefix}-desc`;\n const formatId = `${idPrefix}-format`;\n\n if (!initial) {\n return (\n <>\n <header className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Block (advanced)\n </header>\n <p className=\"pz:text-xs pz:text-muted-foreground\">\n This block uses a non-standard form. Edit the raw text in the editor.\n </p>\n <pre className=\"pz:overflow-x-auto pz:rounded-sm pz:bg-muted pz:px-2 pz:py-1 pz:text-xs\">\n {raw}\n </pre>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n }\n\n function save() {\n if (!name.trim()) return;\n const escaped = description.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n const parts = [`{% output ${name.trim()}`, `type: \"${type}\"`];\n if (type === \"json\" && schema.trim()) parts.push(`schema: \"${schema.trim()}\"`);\n if (format && formatOptions.includes(format)) parts.push(`format: \"${format}\"`);\n parts.push(`description: \"${escaped}\"`);\n if (initial?.required !== undefined) parts.push(`required: ${initial.required}`);\n replaceWith(`${parts.join(\", \")} %}`);\n }\n\n return (\n <>\n <header className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Output declaration\n </header>\n <label htmlFor={nameId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Name</span>\n <Input\n id={nameId}\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"FIELD_NAME\"\n />\n </label>\n <label htmlFor={typeId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Type</span>\n <select\n id={typeId}\n value={type}\n onChange={(e) => {\n const next = e.target.value as OutputType;\n setType(next);\n // A format that the new type does not take would fail validation.\n if (!(TYPES_TO_FORMATS[next] as readonly string[]).includes(format)) setFormat(\"\");\n }}\n className=\"pz:rounded-sm pz:border pz:border-input pz:bg-transparent pz:px-2 pz:py-1 pz:text-sm\"\n >\n <option value=\"string\">string</option>\n <option value=\"number\">number</option>\n <option value=\"boolean\">boolean</option>\n <option value=\"json\">json</option>\n <option value=\"unknown\">unknown</option>\n </select>\n </label>\n {type === \"json\" && (\n <label htmlFor={schemaId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Schema</span>\n <Input\n id={schemaId}\n value={schema}\n onChange={(e) => setSchema(e.target.value)}\n placeholder=\"SCHEMA_NAME\"\n />\n </label>\n )}\n {type !== \"json\" && formatOptions.length > 0 && (\n <label htmlFor={formatId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Format</span>\n <select\n id={formatId}\n value={format}\n onChange={(e) => setFormat(e.target.value)}\n className=\"pz:rounded-sm pz:border pz:border-input pz:bg-transparent pz:px-2 pz:py-1 pz:text-sm\"\n >\n <option value=\"\">none</option>\n {formatOptions.map((f) => (\n <option key={f} value={f}>\n {f}\n </option>\n ))}\n </select>\n </label>\n )}\n <label htmlFor={descId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Description</span>\n <Input id={descId} value={description} onChange={(e) => setDescription(e.target.value)} />\n </label>\n <div className=\"pz:flex pz:items-center pz:justify-between pz:gap-2 pz:pt-1\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={save}>\n Save\n </Button>\n </div>\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChipEditPopover.mjs","names":[],"sources":["../../../../src/components/internal/LiquidEditor/ChipEditPopover.tsx"],"sourcesContent":["import { offset, shift } from \"@floating-ui/react\";\nimport { TYPES_TO_FORMATS } from \"@pipe0/base\";\nimport type { Editor } from \"@tiptap/react\";\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { useFloatingElement } from \"../../../hooks/use-floating-element.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Input } from \"../../ui/input.js\";\nimport { Switch } from \"../../ui/switch.js\";\nimport { Trash2 } from \"../icons.js\";\nimport type { ChipKind } from \"../tag-chip-decoration.js\";\n\nconst SIMPLE_FIELD_RE = /^\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_.]*)\\s*(\\|\\s*default:\\s*\"\")?\\s*\\}\\}$/;\nconst SECRET_RE = /^\\{\\{\\s*SECRETS\\.([A-Z][A-Z0-9_]*)\\s*\\}\\}$/;\nconst CONSTANT_RE = /^\\{\\{\\s*CONSTANTS\\.([A-Z][A-Z0-9_]*)\\s*\\}\\}$/;\n// `{% output NAME, key: \"value\", key: true, ... %}` in any argument order. The\n// args are split out by OUTPUT_ARG_RE; BlockChipBody only edits the keys it\n// knows and falls back to the raw \"advanced\" view for anything else.\nconst OUTPUT_BLOCK_RE =\n /^\\{%\\s*output\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*((?:,\\s*[a-z_]+:\\s*(?:\"(?:[^\"\\\\]|\\\\.)*\"|true|false)\\s*)*)%\\}$/;\nconst OUTPUT_ARG_RE = /([a-z_]+):\\s*(\"(?:[^\"\\\\]|\\\\.)*\"|true|false)/g;\nconst OUTPUT_KEYS = new Set([\"type\", \"schema\", \"description\", \"format\", \"required\"]);\n\ntype OutputType = \"string\" | \"number\" | \"boolean\" | \"json\" | \"unknown\";\n\nfunction parseOutputBlock(raw: string) {\n const m = raw.match(OUTPUT_BLOCK_RE);\n if (!m) return null;\n const args: Record<string, string | boolean> = {};\n for (const [, key, value] of (m[2] ?? \"\").matchAll(OUTPUT_ARG_RE)) {\n if (!key || !value || !OUTPUT_KEYS.has(key)) return null;\n args[key] =\n value === \"true\" || value === \"false\"\n ? value === \"true\"\n : value.slice(1, -1).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n const type = args.type;\n if (typeof type !== \"string\" || !(type in TYPES_TO_FORMATS)) return null;\n // A hand-written format the type does not take has no option to show; keep\n // the raw view rather than silently dropping it on save.\n const format = args.format;\n if (\n typeof format === \"string\" &&\n type !== \"json\" &&\n !(TYPES_TO_FORMATS[type as OutputType] as readonly string[]).includes(format)\n ) {\n return null;\n }\n return {\n name: m[1] ?? \"\",\n type: type as OutputType,\n schema: typeof args.schema === \"string\" ? args.schema : \"\",\n description: typeof args.description === \"string\" ? args.description : \"\",\n format: typeof args.format === \"string\" ? args.format : \"\",\n required: typeof args.required === \"boolean\" ? args.required : undefined,\n };\n}\n\nexport interface ChipEditTarget {\n kind: ChipKind;\n raw: string;\n from: number;\n to: number;\n rect: DOMRect;\n}\n\n/**\n * Contextual popover anchored to a chip's DOM rect. Different content per\n * chip kind:\n * - field: switch for `| default: \"\"` (simple cases only), remove.\n * - secret: publicId header, manage/remove buttons. Power users still\n * edit by hand.\n * - block (output declaration): name / type / format / description editors.\n *\n * On any mutation, the popover dispatches a single replaceWith transaction\n * over the chip's range, then closes itself. The Liquid serialization\n * stays identical to what would have been authored by hand.\n */\nexport function ChipEditPopover({\n editor,\n target,\n onClose,\n}: {\n editor: Editor;\n target: ChipEditTarget;\n onClose: () => void;\n}) {\n const { ref, style, isMounted, getFloatingProps } = useFloatingElement(true, target.rect, 1100, {\n placement: \"bottom-start\",\n middleware: [offset(6), shift()],\n onOpenChange(open) {\n if (!open) onClose();\n },\n dismissOptions: { outsidePress: true, outsidePressEvent: \"mousedown\" },\n });\n\n const popupRef = useRef<HTMLDivElement | null>(null);\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") onClose();\n }\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [onClose]);\n\n function replaceWith(next: string) {\n editor.chain().focus().insertContentAt({ from: target.from, to: target.to }, next).run();\n onClose();\n }\n\n function remove() {\n editor.chain().focus().deleteRange({ from: target.from, to: target.to }).run();\n onClose();\n }\n\n if (!isMounted) return null;\n\n return (\n <div\n ref={(el) => {\n popupRef.current = el;\n ref(el);\n }}\n style={style}\n {...getFloatingProps()}\n onMouseDown={(e) => e.stopPropagation()}\n className=\"pz:flex pz:flex-col pz:gap-2 pz:rounded-md pz:border pz:border-input pz:bg-popover pz:p-3 pz:text-popover-foreground pz:shadow-md pz:min-w-72\"\n role=\"dialog\"\n aria-label={`Edit ${target.kind} chip`}\n >\n {target.kind === \"field\" && (\n <FieldChipBody raw={target.raw} replaceWith={replaceWith} remove={remove} />\n )}\n {target.kind === \"secret\" && <SecretChipBody raw={target.raw} remove={remove} />}\n {target.kind === \"constant\" && <ConstantChipBody raw={target.raw} remove={remove} />}\n {target.kind === \"block\" && (\n <BlockChipBody raw={target.raw} replaceWith={replaceWith} remove={remove} />\n )}\n </div>\n );\n}\n\nfunction FieldChipBody({\n raw,\n replaceWith,\n remove,\n}: {\n raw: string;\n replaceWith: (next: string) => void;\n remove: () => void;\n}) {\n const match = raw.match(SIMPLE_FIELD_RE);\n const name = match?.[1];\n const allowEmpty = !!match?.[2];\n const isSimple = !!match;\n\n if (!isSimple || !name) {\n return (\n <>\n <header className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Field reference (advanced)\n </header>\n <p className=\"pz:text-xs pz:text-muted-foreground\">\n This chip uses a custom Liquid filter chain. Edit the raw text in the editor to change it.\n </p>\n <pre className=\"pz:overflow-x-auto pz:rounded-sm pz:bg-muted pz:px-2 pz:py-1 pz:text-xs\">\n {raw}\n </pre>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n }\n\n function toggleAllowEmpty() {\n replaceWith(allowEmpty ? `{{ ${name} }}` : `{{ ${name} | default: \"\" }}`);\n }\n\n return (\n <>\n <header className=\"pz:flex pz:items-center pz:justify-between pz:gap-2\">\n <span className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">Field reference</span>\n <code className=\"pz:text-xs pz:text-sky-700\">{name}</code>\n </header>\n <div className=\"pz:flex pz:items-center pz:justify-between pz:gap-3 pz:text-sm\">\n <span className=\"pz:flex pz:flex-col\">\n <span>Allow empty</span>\n <span className=\"pz:text-xs pz:text-muted-foreground\">\n Inserts <code>| default: \"\"</code> so missing values render as empty.\n </span>\n </span>\n <Switch checked={allowEmpty} onCheckedChange={toggleAllowEmpty} />\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n}\n\nfunction SecretChipBody({ raw, remove }: { raw: string; remove: () => void }) {\n const publicId = raw.match(SECRET_RE)?.[1];\n\n return (\n <>\n <header className=\"pz:flex pz:items-center pz:justify-between pz:gap-2\">\n <span className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">Secret reference</span>\n <code className=\"pz:text-xs pz:text-amber-700\">{publicId ?? \"(unknown)\"}</code>\n </header>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n}\n\nfunction ConstantChipBody({ raw, remove }: { raw: string; remove: () => void }) {\n const publicId = raw.match(CONSTANT_RE)?.[1];\n\n return (\n <>\n <header className=\"pz:flex pz:items-center pz:justify-between pz:gap-2\">\n <span className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Constant reference\n </span>\n <code className=\"pz:text-xs pz:text-emerald-700\">{publicId ?? \"(unknown)\"}</code>\n </header>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n}\n\nfunction BlockChipBody({\n raw,\n replaceWith,\n remove,\n}: {\n raw: string;\n replaceWith: (next: string) => void;\n remove: () => void;\n}) {\n const initial = useMemo(() => parseOutputBlock(raw), [raw]);\n\n const [name, setName] = useState(initial?.name ?? \"\");\n const [type, setType] = useState<OutputType>(initial?.type ?? \"string\");\n const [schema, setSchema] = useState(initial?.schema ?? \"\");\n const [description, setDescription] = useState(initial?.description ?? \"\");\n const [format, setFormat] = useState(initial?.format ?? \"\");\n const formatOptions: readonly string[] = TYPES_TO_FORMATS[type];\n const idPrefix = useId();\n const nameId = `${idPrefix}-name`;\n const typeId = `${idPrefix}-type`;\n const schemaId = `${idPrefix}-schema`;\n const descId = `${idPrefix}-desc`;\n const formatId = `${idPrefix}-format`;\n\n if (!initial) {\n return (\n <>\n <header className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Block (advanced)\n </header>\n <p className=\"pz:text-xs pz:text-muted-foreground\">\n This block uses a non-standard form. Edit the raw text in the editor.\n </p>\n <pre className=\"pz:overflow-x-auto pz:rounded-sm pz:bg-muted pz:px-2 pz:py-1 pz:text-xs\">\n {raw}\n </pre>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n </>\n );\n }\n\n function save() {\n if (!name.trim()) return;\n const escaped = description.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n const parts = [`{% output ${name.trim()}`, `type: \"${type}\"`];\n if (type === \"json\" && schema.trim()) parts.push(`schema: \"${schema.trim()}\"`);\n if (format && formatOptions.includes(format)) parts.push(`format: \"${format}\"`);\n parts.push(`description: \"${escaped}\"`);\n if (initial?.required !== undefined) parts.push(`required: ${initial.required}`);\n replaceWith(`${parts.join(\", \")} %}`);\n }\n\n return (\n <>\n <header className=\"pz:text-xs pz:font-medium pz:text-muted-foreground\">\n Output declaration\n </header>\n <label htmlFor={nameId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Name</span>\n <Input\n id={nameId}\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"FIELD_NAME\"\n />\n </label>\n <label htmlFor={typeId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Type</span>\n <select\n id={typeId}\n value={type}\n onChange={(e) => {\n const next = e.target.value as OutputType;\n setType(next);\n // A format that the new type does not take would fail validation.\n if (!(TYPES_TO_FORMATS[next] as readonly string[]).includes(format)) setFormat(\"\");\n }}\n className=\"pz:rounded-sm pz:border pz:border-input pz:bg-transparent pz:px-2 pz:py-1 pz:text-sm\"\n >\n <option value=\"string\">string</option>\n <option value=\"number\">number</option>\n <option value=\"boolean\">boolean</option>\n <option value=\"json\">json</option>\n <option value=\"unknown\">unknown</option>\n </select>\n </label>\n {type === \"json\" && (\n <label htmlFor={schemaId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Schema</span>\n <Input\n id={schemaId}\n value={schema}\n onChange={(e) => setSchema(e.target.value)}\n placeholder=\"SCHEMA_NAME\"\n />\n </label>\n )}\n {type !== \"json\" && formatOptions.length > 0 && (\n <label htmlFor={formatId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Format</span>\n <select\n id={formatId}\n value={format}\n onChange={(e) => setFormat(e.target.value)}\n className=\"pz:rounded-sm pz:border pz:border-input pz:bg-transparent pz:px-2 pz:py-1 pz:text-sm\"\n >\n <option value=\"\">none</option>\n {formatOptions.map((f) => (\n <option key={f} value={f}>\n {f}\n </option>\n ))}\n </select>\n </label>\n )}\n <label htmlFor={descId} className=\"pz:flex pz:flex-col pz:gap-1 pz:text-xs\">\n <span>Description</span>\n <Input id={descId} value={description} onChange={(e) => setDescription(e.target.value)} />\n </label>\n <div className=\"pz:flex pz:items-center pz:justify-between pz:gap-2 pz:pt-1\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={remove}>\n <Trash2 className=\"pz:size-3.5 pz:mr-1\" />\n Remove\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={save}>\n Save\n </Button>\n </div>\n </>\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
+ import { Hash, Key } from "../icons.mjs";
1
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { Hash, Key } from "lucide-react";
3
3
 
4
4
  //#region src/components/internal/LiquidEditor/UnifiedReferencePicker.tsx
5
5
  const PREFIX_RE = /^([fsc])\/(.*)$/;
@@ -1 +1 @@
1
- {"version":3,"file":"UnifiedReferencePicker.mjs","names":[],"sources":["../../../../src/components/internal/LiquidEditor/UnifiedReferencePicker.tsx"],"sourcesContent":["import type { PipesFieldDefinitionWithName } from \"@pipe0/base\";\nimport { Hash, Key } from \"lucide-react\";\nimport type { ConstantSuggestion, SecretSuggestion } from \"../../../types/field-props.js\";\nimport type {\n SuggestionItem,\n SuggestionMenuRenderProps,\n} from \"../suggestion-menu/suggestion-menu-types.js\";\n\nexport const SECTION_CAP = 5;\n\nexport type ReferenceKind = \"field\" | \"secret\" | \"constant\";\n\nexport interface ReferenceContext {\n kind: ReferenceKind;\n}\n\nexport interface ReferenceSources {\n inputFields: PipesFieldDefinitionWithName[];\n secretSuggestions: SecretSuggestion[];\n constantSuggestions: ConstantSuggestion[];\n}\n\nconst PREFIX_RE = /^([fsc])\\/(.*)$/;\n\n/**\n * Parses a typed query into `(prefix, residual)`. Lets users filter\n * directly to a single source: `f/email`, `s/STRIPE`, `c/REGION`.\n * Without a prefix, all sources are searched.\n */\nexport function parseQuery(query: string): {\n prefix: ReferenceKind | null;\n residual: string;\n} {\n const m = query.match(PREFIX_RE);\n if (!m) return { prefix: null, residual: query };\n const map = { f: \"field\", s: \"secret\", c: \"constant\" } as const;\n return { prefix: map[m[1] as \"f\" | \"s\" | \"c\"], residual: m[2] };\n}\n\nfunction score(haystack: string, needle: string): number {\n if (!needle) return 1;\n const h = haystack.toLowerCase();\n const n = needle.toLowerCase();\n if (h === n) return 100;\n if (h.startsWith(n)) return 50;\n if (h.includes(n)) return 10;\n return 0;\n}\n\nfunction buildFieldItem(\n f: PipesFieldDefinitionWithName,\n insertField: (name: string) => string,\n): SuggestionItem<ReferenceContext> {\n return {\n title: f.resolvedName,\n subtext: f.type,\n keywords: [f.type],\n context: { kind: \"field\" },\n onSelect: ({ editor, range }) => {\n editor.chain().focus().insertContentAt(range, insertField(f.resolvedName)).run();\n },\n };\n}\n\nfunction buildSecretItem(s: SecretSuggestion): SuggestionItem<ReferenceContext> {\n return {\n title: s.label,\n subtext: `${s.mask} · ${s.ownershipLevel}${s.providerHint ? ` · ${s.providerHint}` : \"\"}`,\n keywords: [s.publicId, s.providerHint ?? \"\"],\n context: { kind: \"secret\" },\n onSelect: ({ editor, range }) => {\n editor.chain().focus().insertContentAt(range, `{{ SECRETS.${s.publicId} }}`).run();\n },\n };\n}\n\nfunction buildConstantItem(c: ConstantSuggestion): SuggestionItem<ReferenceContext> {\n return {\n title: c.label,\n // Flag multi-line constants — the reference is a single chip here but\n // expands to several lines at render time. A hint only; inserting one into\n // a single-line field is allowed.\n subtext: c.format === \"text\" ? `${c.ownershipLevel} · multi-line` : c.ownershipLevel,\n keywords: [c.publicId],\n context: { kind: \"constant\" },\n onSelect: ({ editor, range }) => {\n editor.chain().focus().insertContentAt(range, `{{ CONSTANTS.${c.publicId} }}`).run();\n },\n };\n}\n\n/**\n * Builds the suggestion list for a given query against the three sources.\n * Behavior:\n * - empty query → returns all items in source order (Fields, then\n * Secrets, then Constants). Header rows are rendered separately by\n * the picker UI — this function returns a flat list of items only.\n * - non-empty query → ranks across all sources with a simple\n * starts-with > includes scoring. Items with a zero score are\n * dropped.\n * - prefix syntax `f/`, `s/`, `c/` filters to a single source before\n * scoring with the residual.\n */\nexport function buildReferenceItems(\n query: string,\n sources: ReferenceSources,\n expectedFieldType: string | undefined,\n insertField: (name: string) => string,\n): SuggestionItem<ReferenceContext>[] {\n const { prefix, residual } = parseQuery(query);\n\n const fieldsAll = expectedFieldType\n ? sources.inputFields.filter((f) => f.type === expectedFieldType)\n : sources.inputFields;\n\n const fieldItems = fieldsAll.map((f) => buildFieldItem(f, insertField));\n const secretItems = sources.secretSuggestions.map(buildSecretItem);\n const constantItems = sources.constantSuggestions.map(buildConstantItem);\n\n const pool: SuggestionItem<ReferenceContext>[] =\n prefix === \"field\"\n ? fieldItems\n : prefix === \"secret\"\n ? secretItems\n : prefix === \"constant\"\n ? constantItems\n : [...fieldItems, ...secretItems, ...constantItems];\n\n if (!residual.trim()) return pool;\n\n return pool\n .map((item) => {\n const titleScore = score(item.title, residual);\n const keywordScore = (item.keywords ?? []).reduce(\n (best, kw) => Math.max(best, score(kw, residual)),\n 0,\n );\n const subtextScore = item.subtext ? score(item.subtext, residual) * 0.5 : 0;\n const total = Math.max(titleScore, keywordScore, subtextScore);\n return { item, total };\n })\n .filter(({ total }) => total > 0)\n .sort((a, b) => b.total - a.total)\n .map(({ item }) => item);\n}\n\nfunction KindIcon({ kind }: { kind: ReferenceKind }) {\n // Fields are the default category and don't need a marker — the section\n // header and the type column on the right convey the kind.\n if (kind === \"field\") return null;\n if (kind === \"secret\") return <Key className=\"pz:size-3.5 pz:text-amber-700\" aria-hidden />;\n return <Hash className=\"pz:size-3.5 pz:text-violet-700\" aria-hidden />;\n}\n\nfunction SectionHeader({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"pz:px-2 pz:pt-1.5 pz:pb-0.5 pz:text-[10px] pz:font-medium pz:uppercase pz:tracking-wide pz:text-muted-foreground\">\n {children}\n </div>\n );\n}\n\nfunction ItemRow({\n item,\n active,\n onSelect,\n}: {\n item: SuggestionItem<ReferenceContext>;\n active: boolean;\n onSelect: (item: SuggestionItem<ReferenceContext>) => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n onClick={() => onSelect(item)}\n className={[\n \"pz:flex pz:w-full pz:items-center pz:gap-2 pz:rounded-sm pz:px-2 pz:py-1 pz:text-sm pz:text-left pz:cursor-pointer\",\n active\n ? \"pz:bg-accent pz:text-accent-foreground\"\n : \"pz:hover:bg-accent pz:hover:text-accent-foreground\",\n ].join(\" \")}\n >\n <KindIcon kind={item.context?.kind ?? \"field\"} />\n <span className=\"pz:truncate\">{item.title}</span>\n {item.subtext && (\n <span className=\"pz:ml-auto pz:shrink-0 pz:truncate pz:text-xs pz:text-muted-foreground\">\n {item.subtext}\n </span>\n )}\n </button>\n );\n}\n\n/**\n * Picker UI rendered inside the SuggestionMenu's children render-prop.\n *\n * - Empty query → sectioned view (Fields, Secrets, Constants).\n * - Non-empty query → flat ranked list.\n *\n * The selected-index pointer from `SuggestionMenuRenderProps` always maps\n * to the flat `items` order. Sectioned mode uses the same flat order but\n * inserts headers between the boundaries, so highlight tracking still\n * works without bespoke navigation.\n */\nexport interface OverflowCounts {\n field: number;\n secret: number;\n constant: number;\n}\n\nexport function UnifiedReferencePicker({\n items,\n selectedIndex,\n onSelect,\n query,\n hasSecretsResolver,\n hasConstantsResolver,\n inputFieldsCount,\n secretsPending,\n constantsPending,\n overflow,\n}: SuggestionMenuRenderProps<ReferenceContext> & {\n query: string;\n hasSecretsResolver: boolean;\n hasConstantsResolver: boolean;\n inputFieldsCount: number;\n secretsPending: boolean;\n constantsPending: boolean;\n overflow: OverflowCounts;\n}) {\n const { prefix, residual } = parseQuery(query);\n const isFlat = !!residual.trim() || prefix !== null;\n\n if (isFlat) {\n if (items.length === 0) {\n const isPendingForPrefix =\n (prefix === \"secret\" && secretsPending) ||\n (prefix === \"constant\" && constantsPending) ||\n (prefix === null && (secretsPending || constantsPending));\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-0.5 pz:p-1 pz:min-w-72\" role=\"listbox\">\n {prefix && (\n <SectionHeader>\n {prefix === \"field\" ? \"Fields\" : prefix === \"secret\" ? \"Secrets\" : \"Constants\"}\n </SectionHeader>\n )}\n <div className=\"pz:px-2 pz:py-1.5 pz:text-sm pz:text-muted-foreground\">\n {isPendingForPrefix ? \"Searching…\" : describeEmpty(prefix)}\n </div>\n </div>\n );\n }\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-0.5 pz:p-1 pz:min-w-72\" role=\"listbox\">\n {prefix && (\n <SectionHeader>\n {prefix === \"field\" ? \"Fields\" : prefix === \"secret\" ? \"Secrets\" : \"Constants\"}\n </SectionHeader>\n )}\n {items.map((item, index) => (\n <ItemRow\n key={`${item.context?.kind}-${item.title}`}\n item={item}\n active={selectedIndex === index}\n onSelect={onSelect}\n />\n ))}\n </div>\n );\n }\n\n // Sectioned view — always render Fields and Secrets headers in fixed\n // order, with empty-state hints when a section has no items. Constants\n // only appear when there are any to show.\n //\n // Items here are pre-capped per kind (LiquidEditor applies SECTION_CAP).\n // The selectedIndex maps to this capped flat order — no hidden rows.\n const fieldItems = items.filter((it) => it.context?.kind === \"field\");\n const secretItems = items.filter((it) => it.context?.kind === \"secret\");\n const constantItems = items.filter((it) => it.context?.kind === \"constant\");\n\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-0.5 pz:p-1 pz:min-w-72\" role=\"listbox\">\n <Section\n label=\"Fields\"\n kind=\"field\"\n items={fieldItems}\n startIndex={0}\n selectedIndex={selectedIndex}\n onSelect={onSelect}\n emptyHint={\n inputFieldsCount === 0 ? \"No input fields available.\" : \"No matching input fields.\"\n }\n overflow={overflow.field}\n overflowHelper=\"Type to filter.\"\n />\n {hasSecretsResolver && (\n <Section\n label=\"Secrets\"\n kind=\"secret\"\n items={secretItems}\n startIndex={fieldItems.length}\n selectedIndex={selectedIndex}\n onSelect={onSelect}\n emptyHint={secretsPending ? \"Searching…\" : \"No matching secrets.\"}\n helperHint=\"Type /s/ to filter to secrets only.\"\n pending={secretsPending}\n overflow={overflow.secret}\n overflowHelper=\"Type to filter, or /s/ for secrets only.\"\n />\n )}\n {hasConstantsResolver && (\n <Section\n label=\"Constants\"\n kind=\"constant\"\n items={constantItems}\n startIndex={fieldItems.length + secretItems.length}\n selectedIndex={selectedIndex}\n onSelect={onSelect}\n emptyHint={constantsPending ? \"Searching…\" : \"No matching constants.\"}\n helperHint=\"Type /c/ to filter to constants only.\"\n pending={constantsPending}\n overflow={overflow.constant}\n overflowHelper=\"Type to filter, or /c/ for constants only.\"\n />\n )}\n </div>\n );\n}\n\nfunction Section({\n label,\n kind,\n items,\n startIndex,\n selectedIndex,\n onSelect,\n emptyHint,\n helperHint,\n pending,\n overflow = 0,\n overflowHelper,\n}: {\n label: string;\n kind: ReferenceKind;\n items: SuggestionItem<ReferenceContext>[];\n startIndex: number;\n selectedIndex?: number;\n onSelect: (item: SuggestionItem<ReferenceContext>) => void;\n emptyHint: string;\n helperHint?: string;\n pending?: boolean;\n overflow?: number;\n overflowHelper?: string;\n}) {\n return (\n <div className=\"pz:flex pz:flex-col\">\n <SectionHeader>\n {label}\n {items.length > 0 && (\n <span className=\"pz:ml-1 pz:normal-case pz:tracking-normal\">\n ({items.length}\n {overflow > 0 && ` of ${items.length + overflow}`})\n </span>\n )}\n {pending && (\n <span className=\"pz:ml-1 pz:normal-case pz:tracking-normal pz:opacity-70\">\n · Searching…\n </span>\n )}\n </SectionHeader>\n {items.length === 0 ? (\n <div className=\"pz:px-2 pz:py-1 pz:text-xs pz:text-muted-foreground\">\n {emptyHint}\n {helperHint && <span className=\"pz:block pz:opacity-70\">{helperHint}</span>}\n </div>\n ) : (\n <>\n {items.map((item, i) => (\n <ItemRow\n key={`${kind}-${item.title}`}\n item={item}\n active={selectedIndex === startIndex + i}\n onSelect={onSelect}\n />\n ))}\n {overflow > 0 && overflowHelper && (\n <div className=\"pz:px-2 pz:py-0.5 pz:text-[11px] pz:text-muted-foreground pz:opacity-70\">\n +{overflow} more — {overflowHelper}\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nfunction describeEmpty(prefix: ReferenceKind | null): string {\n if (prefix === \"secret\") return \"No matching secrets.\";\n if (prefix === \"constant\") return \"No matching constants.\";\n if (prefix === \"field\") return \"No matching fields.\";\n return \"No matches.\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"UnifiedReferencePicker.mjs","names":[],"sources":["../../../../src/components/internal/LiquidEditor/UnifiedReferencePicker.tsx"],"sourcesContent":["import type { PipesFieldDefinitionWithName } from \"@pipe0/base\";\nimport type { ConstantSuggestion, SecretSuggestion } from \"../../../types/field-props.js\";\nimport { Hash, Key } from \"../icons.js\";\nimport type {\n SuggestionItem,\n SuggestionMenuRenderProps,\n} from \"../suggestion-menu/suggestion-menu-types.js\";\n\nexport const SECTION_CAP = 5;\n\nexport type ReferenceKind = \"field\" | \"secret\" | \"constant\";\n\nexport interface ReferenceContext {\n kind: ReferenceKind;\n}\n\nexport interface ReferenceSources {\n inputFields: PipesFieldDefinitionWithName[];\n secretSuggestions: SecretSuggestion[];\n constantSuggestions: ConstantSuggestion[];\n}\n\nconst PREFIX_RE = /^([fsc])\\/(.*)$/;\n\n/**\n * Parses a typed query into `(prefix, residual)`. Lets users filter\n * directly to a single source: `f/email`, `s/STRIPE`, `c/REGION`.\n * Without a prefix, all sources are searched.\n */\nexport function parseQuery(query: string): {\n prefix: ReferenceKind | null;\n residual: string;\n} {\n const m = query.match(PREFIX_RE);\n if (!m) return { prefix: null, residual: query };\n const map = { f: \"field\", s: \"secret\", c: \"constant\" } as const;\n return { prefix: map[m[1] as \"f\" | \"s\" | \"c\"], residual: m[2] };\n}\n\nfunction score(haystack: string, needle: string): number {\n if (!needle) return 1;\n const h = haystack.toLowerCase();\n const n = needle.toLowerCase();\n if (h === n) return 100;\n if (h.startsWith(n)) return 50;\n if (h.includes(n)) return 10;\n return 0;\n}\n\nfunction buildFieldItem(\n f: PipesFieldDefinitionWithName,\n insertField: (name: string) => string,\n): SuggestionItem<ReferenceContext> {\n return {\n title: f.resolvedName,\n subtext: f.type,\n keywords: [f.type],\n context: { kind: \"field\" },\n onSelect: ({ editor, range }) => {\n editor.chain().focus().insertContentAt(range, insertField(f.resolvedName)).run();\n },\n };\n}\n\nfunction buildSecretItem(s: SecretSuggestion): SuggestionItem<ReferenceContext> {\n return {\n title: s.label,\n subtext: `${s.mask} · ${s.ownershipLevel}${s.providerHint ? ` · ${s.providerHint}` : \"\"}`,\n keywords: [s.publicId, s.providerHint ?? \"\"],\n context: { kind: \"secret\" },\n onSelect: ({ editor, range }) => {\n editor.chain().focus().insertContentAt(range, `{{ SECRETS.${s.publicId} }}`).run();\n },\n };\n}\n\nfunction buildConstantItem(c: ConstantSuggestion): SuggestionItem<ReferenceContext> {\n return {\n title: c.label,\n // Flag multi-line constants — the reference is a single chip here but\n // expands to several lines at render time. A hint only; inserting one into\n // a single-line field is allowed.\n subtext: c.format === \"text\" ? `${c.ownershipLevel} · multi-line` : c.ownershipLevel,\n keywords: [c.publicId],\n context: { kind: \"constant\" },\n onSelect: ({ editor, range }) => {\n editor.chain().focus().insertContentAt(range, `{{ CONSTANTS.${c.publicId} }}`).run();\n },\n };\n}\n\n/**\n * Builds the suggestion list for a given query against the three sources.\n * Behavior:\n * - empty query → returns all items in source order (Fields, then\n * Secrets, then Constants). Header rows are rendered separately by\n * the picker UI — this function returns a flat list of items only.\n * - non-empty query → ranks across all sources with a simple\n * starts-with > includes scoring. Items with a zero score are\n * dropped.\n * - prefix syntax `f/`, `s/`, `c/` filters to a single source before\n * scoring with the residual.\n */\nexport function buildReferenceItems(\n query: string,\n sources: ReferenceSources,\n expectedFieldType: string | undefined,\n insertField: (name: string) => string,\n): SuggestionItem<ReferenceContext>[] {\n const { prefix, residual } = parseQuery(query);\n\n const fieldsAll = expectedFieldType\n ? sources.inputFields.filter((f) => f.type === expectedFieldType)\n : sources.inputFields;\n\n const fieldItems = fieldsAll.map((f) => buildFieldItem(f, insertField));\n const secretItems = sources.secretSuggestions.map(buildSecretItem);\n const constantItems = sources.constantSuggestions.map(buildConstantItem);\n\n const pool: SuggestionItem<ReferenceContext>[] =\n prefix === \"field\"\n ? fieldItems\n : prefix === \"secret\"\n ? secretItems\n : prefix === \"constant\"\n ? constantItems\n : [...fieldItems, ...secretItems, ...constantItems];\n\n if (!residual.trim()) return pool;\n\n return pool\n .map((item) => {\n const titleScore = score(item.title, residual);\n const keywordScore = (item.keywords ?? []).reduce(\n (best, kw) => Math.max(best, score(kw, residual)),\n 0,\n );\n const subtextScore = item.subtext ? score(item.subtext, residual) * 0.5 : 0;\n const total = Math.max(titleScore, keywordScore, subtextScore);\n return { item, total };\n })\n .filter(({ total }) => total > 0)\n .sort((a, b) => b.total - a.total)\n .map(({ item }) => item);\n}\n\nfunction KindIcon({ kind }: { kind: ReferenceKind }) {\n // Fields are the default category and don't need a marker — the section\n // header and the type column on the right convey the kind.\n if (kind === \"field\") return null;\n if (kind === \"secret\") return <Key className=\"pz:size-3.5 pz:text-amber-700\" aria-hidden />;\n return <Hash className=\"pz:size-3.5 pz:text-violet-700\" aria-hidden />;\n}\n\nfunction SectionHeader({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"pz:px-2 pz:pt-1.5 pz:pb-0.5 pz:text-[10px] pz:font-medium pz:uppercase pz:tracking-wide pz:text-muted-foreground\">\n {children}\n </div>\n );\n}\n\nfunction ItemRow({\n item,\n active,\n onSelect,\n}: {\n item: SuggestionItem<ReferenceContext>;\n active: boolean;\n onSelect: (item: SuggestionItem<ReferenceContext>) => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n onClick={() => onSelect(item)}\n className={[\n \"pz:flex pz:w-full pz:items-center pz:gap-2 pz:rounded-sm pz:px-2 pz:py-1 pz:text-sm pz:text-left pz:cursor-pointer\",\n active\n ? \"pz:bg-accent pz:text-accent-foreground\"\n : \"pz:hover:bg-accent pz:hover:text-accent-foreground\",\n ].join(\" \")}\n >\n <KindIcon kind={item.context?.kind ?? \"field\"} />\n <span className=\"pz:truncate\">{item.title}</span>\n {item.subtext && (\n <span className=\"pz:ml-auto pz:shrink-0 pz:truncate pz:text-xs pz:text-muted-foreground\">\n {item.subtext}\n </span>\n )}\n </button>\n );\n}\n\n/**\n * Picker UI rendered inside the SuggestionMenu's children render-prop.\n *\n * - Empty query → sectioned view (Fields, Secrets, Constants).\n * - Non-empty query → flat ranked list.\n *\n * The selected-index pointer from `SuggestionMenuRenderProps` always maps\n * to the flat `items` order. Sectioned mode uses the same flat order but\n * inserts headers between the boundaries, so highlight tracking still\n * works without bespoke navigation.\n */\nexport interface OverflowCounts {\n field: number;\n secret: number;\n constant: number;\n}\n\nexport function UnifiedReferencePicker({\n items,\n selectedIndex,\n onSelect,\n query,\n hasSecretsResolver,\n hasConstantsResolver,\n inputFieldsCount,\n secretsPending,\n constantsPending,\n overflow,\n}: SuggestionMenuRenderProps<ReferenceContext> & {\n query: string;\n hasSecretsResolver: boolean;\n hasConstantsResolver: boolean;\n inputFieldsCount: number;\n secretsPending: boolean;\n constantsPending: boolean;\n overflow: OverflowCounts;\n}) {\n const { prefix, residual } = parseQuery(query);\n const isFlat = !!residual.trim() || prefix !== null;\n\n if (isFlat) {\n if (items.length === 0) {\n const isPendingForPrefix =\n (prefix === \"secret\" && secretsPending) ||\n (prefix === \"constant\" && constantsPending) ||\n (prefix === null && (secretsPending || constantsPending));\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-0.5 pz:p-1 pz:min-w-72\" role=\"listbox\">\n {prefix && (\n <SectionHeader>\n {prefix === \"field\" ? \"Fields\" : prefix === \"secret\" ? \"Secrets\" : \"Constants\"}\n </SectionHeader>\n )}\n <div className=\"pz:px-2 pz:py-1.5 pz:text-sm pz:text-muted-foreground\">\n {isPendingForPrefix ? \"Searching…\" : describeEmpty(prefix)}\n </div>\n </div>\n );\n }\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-0.5 pz:p-1 pz:min-w-72\" role=\"listbox\">\n {prefix && (\n <SectionHeader>\n {prefix === \"field\" ? \"Fields\" : prefix === \"secret\" ? \"Secrets\" : \"Constants\"}\n </SectionHeader>\n )}\n {items.map((item, index) => (\n <ItemRow\n key={`${item.context?.kind}-${item.title}`}\n item={item}\n active={selectedIndex === index}\n onSelect={onSelect}\n />\n ))}\n </div>\n );\n }\n\n // Sectioned view — always render Fields and Secrets headers in fixed\n // order, with empty-state hints when a section has no items. Constants\n // only appear when there are any to show.\n //\n // Items here are pre-capped per kind (LiquidEditor applies SECTION_CAP).\n // The selectedIndex maps to this capped flat order — no hidden rows.\n const fieldItems = items.filter((it) => it.context?.kind === \"field\");\n const secretItems = items.filter((it) => it.context?.kind === \"secret\");\n const constantItems = items.filter((it) => it.context?.kind === \"constant\");\n\n return (\n <div className=\"pz:flex pz:flex-col pz:gap-0.5 pz:p-1 pz:min-w-72\" role=\"listbox\">\n <Section\n label=\"Fields\"\n kind=\"field\"\n items={fieldItems}\n startIndex={0}\n selectedIndex={selectedIndex}\n onSelect={onSelect}\n emptyHint={\n inputFieldsCount === 0 ? \"No input fields available.\" : \"No matching input fields.\"\n }\n overflow={overflow.field}\n overflowHelper=\"Type to filter.\"\n />\n {hasSecretsResolver && (\n <Section\n label=\"Secrets\"\n kind=\"secret\"\n items={secretItems}\n startIndex={fieldItems.length}\n selectedIndex={selectedIndex}\n onSelect={onSelect}\n emptyHint={secretsPending ? \"Searching…\" : \"No matching secrets.\"}\n helperHint=\"Type /s/ to filter to secrets only.\"\n pending={secretsPending}\n overflow={overflow.secret}\n overflowHelper=\"Type to filter, or /s/ for secrets only.\"\n />\n )}\n {hasConstantsResolver && (\n <Section\n label=\"Constants\"\n kind=\"constant\"\n items={constantItems}\n startIndex={fieldItems.length + secretItems.length}\n selectedIndex={selectedIndex}\n onSelect={onSelect}\n emptyHint={constantsPending ? \"Searching…\" : \"No matching constants.\"}\n helperHint=\"Type /c/ to filter to constants only.\"\n pending={constantsPending}\n overflow={overflow.constant}\n overflowHelper=\"Type to filter, or /c/ for constants only.\"\n />\n )}\n </div>\n );\n}\n\nfunction Section({\n label,\n kind,\n items,\n startIndex,\n selectedIndex,\n onSelect,\n emptyHint,\n helperHint,\n pending,\n overflow = 0,\n overflowHelper,\n}: {\n label: string;\n kind: ReferenceKind;\n items: SuggestionItem<ReferenceContext>[];\n startIndex: number;\n selectedIndex?: number;\n onSelect: (item: SuggestionItem<ReferenceContext>) => void;\n emptyHint: string;\n helperHint?: string;\n pending?: boolean;\n overflow?: number;\n overflowHelper?: string;\n}) {\n return (\n <div className=\"pz:flex pz:flex-col\">\n <SectionHeader>\n {label}\n {items.length > 0 && (\n <span className=\"pz:ml-1 pz:normal-case pz:tracking-normal\">\n ({items.length}\n {overflow > 0 && ` of ${items.length + overflow}`})\n </span>\n )}\n {pending && (\n <span className=\"pz:ml-1 pz:normal-case pz:tracking-normal pz:opacity-70\">\n · Searching…\n </span>\n )}\n </SectionHeader>\n {items.length === 0 ? (\n <div className=\"pz:px-2 pz:py-1 pz:text-xs pz:text-muted-foreground\">\n {emptyHint}\n {helperHint && <span className=\"pz:block pz:opacity-70\">{helperHint}</span>}\n </div>\n ) : (\n <>\n {items.map((item, i) => (\n <ItemRow\n key={`${kind}-${item.title}`}\n item={item}\n active={selectedIndex === startIndex + i}\n onSelect={onSelect}\n />\n ))}\n {overflow > 0 && overflowHelper && (\n <div className=\"pz:px-2 pz:py-0.5 pz:text-[11px] pz:text-muted-foreground pz:opacity-70\">\n +{overflow} more — {overflowHelper}\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nfunction describeEmpty(prefix: ReferenceKind | null): string {\n if (prefix === \"secret\") return \"No matching secrets.\";\n if (prefix === \"constant\") return \"No matching constants.\";\n if (prefix === \"field\") return \"No matching fields.\";\n return \"No matches.\";\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../../lib/utils.mjs";
2
2
  import { Button } from "../../ui/button.mjs";
3
- import { IconMinus, IconPlus } from "../icons.mjs";
3
+ import { IconMinus, IconPlus, X } from "../icons.mjs";
4
4
  import { useDebouncedFn } from "../../../hooks/use-debounced-fn.mjs";
5
5
  import { FieldTypeBadge } from "../../../widgets/field-type-badge.mjs";
6
6
  import { WidgetStrip } from "../../../widgets/widget-strip.mjs";
@@ -8,7 +8,6 @@ import { Combobox, ComboboxChips, ComboboxChipsInput, ComboboxContent, ComboboxE
8
8
  import { filterOptions } from "../multi-select-popover-trigger.mjs";
9
9
  import { useEffect, useMemo, useRef, useState } from "react";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
- import { XIcon } from "lucide-react";
12
11
 
13
12
  //#region src/components/internal/combobox/include-exclude-combobox.tsx
14
13
  const includeChipClass = "pz:flex pz:h-[calc(--spacing(5.25))] pz:w-fit pz:items-center pz:gap-0 pz:rounded-sm pz:border pz:border-border pz:bg-muted pz:pl-0 pz:pr-0 pz:text-xs pz:font-medium pz:whitespace-nowrap pz:text-foreground pz:overflow-hidden";
@@ -246,7 +245,7 @@ function IncludeExcludeCombobox({ include, exclude, onChange, options, loadOptio
246
245
  },
247
246
  "aria-label": `Remove ${chip.label}`,
248
247
  className: cn("pz:opacity-50 pz:hover:opacity-100", isInc ? void 0 : "pz:text-destructive pz:hover:bg-destructive/15"),
249
- children: /* @__PURE__ */ jsx(XIcon, { className: "pz:pointer-events-none" })
248
+ children: /* @__PURE__ */ jsx(X, { className: "pz:pointer-events-none" })
250
249
  })
251
250
  ]
252
251
  }, chip.value);