@pollinations/ui 0.1.0-alpha.1 → 0.1.0-alpha.4

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 (88) hide show
  1. package/README.md +148 -36
  2. package/dist/Button-ByKwRtdw.d.cts +29 -0
  3. package/dist/Button-ByKwRtdw.d.ts +29 -0
  4. package/dist/Chip-v-ADv6cz.d.cts +18 -0
  5. package/dist/Chip-v-ADv6cz.d.ts +18 -0
  6. package/dist/Dialog-D3Nqt8cE.d.cts +66 -0
  7. package/dist/Dialog-D3Nqt8cE.d.ts +66 -0
  8. package/dist/app-user-menu/sdk.cjs +698 -242
  9. package/dist/app-user-menu/sdk.cjs.map +1 -1
  10. package/dist/app-user-menu/sdk.d.cts +22 -7
  11. package/dist/app-user-menu/sdk.d.ts +22 -7
  12. package/dist/app-user-menu/sdk.js +703 -240
  13. package/dist/app-user-menu/sdk.js.map +1 -1
  14. package/dist/app.css +12 -122
  15. package/dist/auth/index.cjs +1365 -85
  16. package/dist/auth/index.cjs.map +1 -1
  17. package/dist/auth/index.d.cts +91 -6
  18. package/dist/auth/index.d.ts +91 -6
  19. package/dist/auth/index.js +1362 -84
  20. package/dist/auth/index.js.map +1 -1
  21. package/dist/auth/sdk.cjs +244 -0
  22. package/dist/auth/sdk.cjs.map +1 -0
  23. package/dist/auth/sdk.d.cts +40 -0
  24. package/dist/auth/sdk.d.ts +40 -0
  25. package/dist/auth/sdk.js +213 -0
  26. package/dist/auth/sdk.js.map +1 -0
  27. package/dist/brand/lockup-horizontal-black.png +0 -0
  28. package/dist/brand/lockup-horizontal-black.svg +4 -0
  29. package/dist/brand/lockup-horizontal-white.png +0 -0
  30. package/dist/brand/lockup-horizontal-white.svg +4 -0
  31. package/dist/brand/lockup-horizontal.svg +4 -0
  32. package/dist/brand/lockup-stacked-black.png +0 -0
  33. package/dist/brand/lockup-stacked-black.svg +4 -0
  34. package/dist/brand/lockup-stacked-white.png +0 -0
  35. package/dist/brand/lockup-stacked-white.svg +4 -0
  36. package/dist/brand/lockup-stacked.svg +4 -0
  37. package/dist/brand/mark-black.png +0 -0
  38. package/dist/brand/mark-black.svg +1 -0
  39. package/dist/brand/mark-white.png +0 -0
  40. package/dist/brand/mark-white.svg +1 -0
  41. package/dist/brand/mark.svg +1 -0
  42. package/dist/brand/polli/polli-lockup-horizontal-black.png +0 -0
  43. package/dist/brand/polli/polli-lockup-horizontal-white.png +0 -0
  44. package/dist/brand/polli/polli-lockup-stacked-black.png +0 -0
  45. package/dist/brand/polli/polli-lockup-stacked-white.png +0 -0
  46. package/dist/brand/polli/polli.png +0 -0
  47. package/dist/brand/wordmark-black.png +0 -0
  48. package/dist/brand/wordmark-black.svg +1 -0
  49. package/dist/brand/wordmark-white.png +0 -0
  50. package/dist/brand/wordmark-white.svg +1 -0
  51. package/dist/brand/wordmark.svg +1 -0
  52. package/dist/fonts/geist-pixel-square.woff2 +0 -0
  53. package/dist/gen/index.cjs +427 -217
  54. package/dist/gen/index.cjs.map +1 -1
  55. package/dist/gen/index.d.cts +68 -19
  56. package/dist/gen/index.d.ts +68 -19
  57. package/dist/gen/index.js +421 -215
  58. package/dist/gen/index.js.map +1 -1
  59. package/dist/index.browser.min.js +2 -0
  60. package/dist/index.browser.min.js.map +1 -0
  61. package/dist/index.cjs +3711 -1590
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +548 -178
  64. package/dist/index.d.ts +548 -178
  65. package/dist/index.js +3713 -1655
  66. package/dist/index.js.map +1 -1
  67. package/dist/licenses/Geist-OFL.txt +93 -0
  68. package/dist/markdown.cjs +826 -0
  69. package/dist/markdown.cjs.map +1 -0
  70. package/dist/markdown.d.cts +16 -0
  71. package/dist/markdown.d.ts +16 -0
  72. package/dist/markdown.js +797 -0
  73. package/dist/markdown.js.map +1 -0
  74. package/dist/styles.css +1 -1
  75. package/dist/theme.css +87 -0
  76. package/dist/wallet/index.cjs +291 -64
  77. package/dist/wallet/index.cjs.map +1 -1
  78. package/dist/wallet/index.d.cts +30 -7
  79. package/dist/wallet/index.d.ts +30 -7
  80. package/dist/wallet/index.js +290 -64
  81. package/dist/wallet/index.js.map +1 -1
  82. package/package.json +106 -89
  83. package/dist/Chip-CQCq34kS.d.cts +0 -20
  84. package/dist/Chip-Cj4xG7Te.d.ts +0 -20
  85. package/dist/assets/logo-wordmark.svg +0 -1
  86. package/dist/assets/logo.svg +0 -4
  87. package/dist/theme-jRPPBmld.d.cts +0 -20
  88. package/dist/theme-jRPPBmld.d.ts +0 -20
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/modules/gen/index.ts","../../src/lib/cn.ts","../../src/primitives/Button.tsx","../../src/primitives/ChevronIcon.tsx","../../src/primitives/Chip.tsx","../../src/primitives/Dropdown.tsx","../../src/primitives/ScrollArea.tsx","../../src/primitives/TabButton.tsx","../../src/modules/gen/themes.ts","../../src/modules/gen/ModelSelector.tsx"],"sourcesContent":["export {\n CATEGORY_LABELS,\n categoryLabel,\n ModelSelector,\n type ModelSelectorCategory,\n type ModelSelectorItem,\n type ModelSelectorProps,\n} from \"./ModelSelector.tsx\";\nexport {\n getModalityTheme,\n type Modality,\n modalityTheme,\n} from \"./themes.ts\";\n","import { clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({ prefix: \"polli\" });\n\n/** Internal class merge for polli:-prefixed package primitive classes. */\nexport function cn(...inputs: (string | undefined | null | false)[]) {\n return twMerge(clsx(...inputs));\n}\n","import type { PropsWithChildren } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\nimport type { ThemeName } from \"../theme.ts\";\n\n/** Button only supports `danger`. Label/status color recipes live on Chip. */\ntype ButtonIntent = \"danger\";\n\nconst sizes = {\n sm: \"polli:px-2 polli:pt-0.5 polli:pb-1\",\n md: \"polli:px-4 polli:pt-1.5 polli:pb-2\",\n lg: \"polli:px-6 polli:py-3\",\n} as const;\n\n// Cascade-driven base — reads [data-theme] vars.\nconst themeClasses =\n \"polli:bg-theme-bg-active polli:text-theme-text-base \" +\n \"polli:hover:bg-theme-bg-hover polli:transition-colors\";\n\n// Single intent: danger. Soft recipe — light tile + deep text, slightly\n// deeper bg on hover. No filled CTAs anywhere.\nconst intentClasses: Record<ButtonIntent, string> = {\n danger:\n \"polli:bg-intent-danger-bg-light polli:text-intent-danger-text \" +\n \"polli:hover:bg-intent-danger-bg-hover polli:transition-colors\",\n};\n\ntype BaseButtonProps = {\n /** Override the cascade theme for this button's subtree. Ignored when `intent` is set. */\n theme?: ThemeName;\n /** Only `\"danger\"` for now. */\n intent?: ButtonIntent;\n size?: keyof typeof sizes;\n className?: string;\n disabled?: boolean;\n};\n\nconst buttonClasses = ({\n theme: _theme,\n intent,\n size,\n className,\n disabled,\n}: BaseButtonProps & { disabled?: boolean }) => {\n const colorClasses = intent ? intentClasses[intent] : themeClasses;\n return cn(\n \"polli-control polli:inline-flex polli:items-center polli:justify-center polli:rounded-full polli:self-center polli:font-medium polli:leading-normal polli:box-border\",\n disabled\n ? \"polli:opacity-50 polli:cursor-not-allowed\"\n : \"polli:hover:filter polli:hover:brightness-105 polli:cursor-pointer\",\n colorClasses,\n sizes[size || \"md\"],\n className,\n );\n};\n\nexport type ButtonProps<T extends React.ElementType = \"button\"> =\n PropsWithChildren<BaseButtonProps> & {\n as?: T;\n } & Omit<React.ComponentPropsWithoutRef<T>, keyof BaseButtonProps | \"as\">;\n\nexport function Button<T extends React.ElementType = \"button\">({\n as,\n children,\n theme,\n intent,\n size,\n className,\n disabled,\n ...buttonProps\n}: ButtonProps<T>) {\n const Component: React.ElementType = as || \"button\";\n\n return (\n <Component\n // Cascade override only applies when `intent` is unset.\n data-theme={intent ? undefined : theme}\n data-intent={intent}\n className={buttonClasses({\n theme,\n intent,\n size,\n className,\n disabled,\n })}\n disabled={disabled}\n {...buttonProps}\n >\n {children}\n </Component>\n );\n}\n","import type { FC } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\ntype ChevronIconProps = {\n /** Rotate 180° when expanded/open. */\n expanded?: boolean;\n className?: string;\n};\n\n/**\n * Canonical chevron used for collapsibles, dropdown triggers, and select pills\n * across the app. One icon everywhere.\n */\nexport const ChevronIcon: FC<ChevronIconProps> = ({ expanded, className }) => (\n <svg\n aria-hidden=\"true\"\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n className={cn(\n \"polli:shrink-0 polli:transition-transform polli:duration-200 polli:ease-out\",\n expanded && \"polli:rotate-180\",\n className,\n )}\n >\n <path\n d=\"M3 4.5 6 7.5l3-3\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"1.5\"\n />\n </svg>\n);\n","import type { ComponentPropsWithoutRef, FC } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\nimport type { ThemeName } from \"../theme.ts\";\n\n/** Semantic chip labels for status and metadata badges. */\ntype ChipIntent =\n | \"news\"\n | \"alpha\"\n | \"neutral\"\n | \"success\"\n | \"warning\"\n | \"danger\";\n\nconst chipSizes = {\n sm: \"polli:px-2 polli:py-0.5 polli:text-xs\",\n md: \"polli:px-2.5 polli:py-0.5 polli:text-sm\",\n lg: \"polli:px-3 polli:py-1 polli:text-sm\",\n} as const;\n\nconst intentClasses: Record<ChipIntent, string> = {\n news: \"polli:bg-intent-news-bg-light polli:text-intent-news-text\",\n alpha: \"polli:bg-intent-alpha-bg-light polli:text-intent-alpha-text\",\n neutral: \"polli:bg-ink-100/80 polli:text-ink-900\",\n success: \"polli:bg-intent-success-bg-light polli:text-intent-success-text\",\n warning: \"polli:bg-intent-warning-bg-light polli:text-intent-warning-text\",\n danger: \"polli:bg-intent-danger-bg-light polli:text-intent-danger-text\",\n};\n\nexport type ChipProps = ComponentPropsWithoutRef<\"span\"> & {\n /** Override the cascade theme for this chip's subtree. */\n theme?: ThemeName;\n /** Semantic intent. Wins over `theme` when set. */\n intent?: ChipIntent;\n size?: keyof typeof chipSizes;\n};\n\n// Static, rectangular colored container. Round shapes are reserved for buttons.\n// Reads `bg-theme-bg-active` / `text-theme-text-strong` from the cascade unless\n// `intent` is set (semantic, theme-independent).\nexport const Chip: FC<ChipProps> = ({\n theme,\n intent,\n size = \"md\",\n className,\n children,\n ...rest\n}) => {\n const intentClass = intent\n ? intentClasses[intent]\n : \"polli:bg-theme-bg-active polli:text-theme-text-strong\";\n return (\n <span\n {...rest}\n data-theme={theme}\n className={cn(\n \"polli:inline-flex polli:shrink-0 polli:items-center polli:gap-1 polli:rounded-lg polli:font-medium polli:leading-normal\",\n chipSizes[size],\n intentClass,\n className,\n )}\n >\n {children}\n </span>\n );\n};\n","import { Popover } from \"@ark-ui/react/popover\";\nimport { Portal } from \"@ark-ui/react/portal\";\nimport type { FC, ReactNode } from \"react\";\nimport { useState } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\nimport type { ThemeName } from \"../theme.ts\";\n\nconst DEFAULT_PANEL =\n \"polli:rounded-lg polli:border polli:border-theme-border polli:bg-theme-bg-pale polli:shadow-lg\";\n\nexport type DropdownProps = {\n theme: ThemeName;\n /** Trigger element; receives the current open state (e.g. to rotate a chevron). */\n trigger: (open: boolean) => ReactNode;\n /** Panel content. As a function, receives `close` to dismiss after a selection. */\n children: ReactNode | ((close: () => void) => ReactNode);\n /** Panel placement relative to the trigger. */\n align?: \"start\" | \"end\";\n /** Controlled open state. Omit to let the Dropdown manage its own. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Appended after the surface (widths, padding, max-height…). */\n className?: string;\n};\n\nexport const Dropdown: FC<DropdownProps> = ({\n theme,\n trigger,\n children,\n align = \"start\",\n open: openProp,\n onOpenChange,\n className,\n}) => {\n const [internalOpen, setInternalOpen] = useState(false);\n const isControlled = openProp !== undefined;\n const open = isControlled ? openProp : internalOpen;\n\n const setOpen = (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n };\n\n return (\n <Popover.Root\n open={open}\n onOpenChange={(details) => setOpen(details.open)}\n positioning={{\n placement: align === \"end\" ? \"bottom-end\" : \"bottom-start\",\n }}\n >\n <Popover.Trigger asChild>{trigger(open)}</Popover.Trigger>\n <Portal>\n <Popover.Positioner>\n <Popover.Content\n data-theme={theme}\n className={cn(\n \"polli:z-50 polli:overflow-hidden polli:focus:outline-none\",\n DEFAULT_PANEL,\n className,\n )}\n >\n {typeof children === \"function\"\n ? children(() => setOpen(false))\n : children}\n </Popover.Content>\n </Popover.Positioner>\n </Portal>\n </Popover.Root>\n );\n};\n","import {\n type ComponentPropsWithoutRef,\n forwardRef,\n type MutableRefObject,\n type PropsWithChildren,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n} from \"react\";\nimport { cn } from \"../lib/cn.ts\";\nimport type { ThemeName } from \"../theme.ts\";\n\ntype ScrollAxis = \"y\" | \"x\";\n\nconst axisClasses: Record<ScrollAxis, string> = {\n y: \"polli:overflow-y-auto polli:overflow-x-hidden\",\n x: \"polli:overflow-x-auto polli:overflow-y-hidden\",\n};\n\ntype ScrollAreaOwnProps = {\n /** Override the ambient cascade theme for this scroller's thumb. */\n theme?: ThemeName;\n /** Scroll direction. Default `y`. */\n axis?: ScrollAxis;\n className?: string;\n};\n\nexport type ScrollAreaProps = PropsWithChildren<\n ScrollAreaOwnProps &\n Omit<ComponentPropsWithoutRef<\"div\">, keyof ScrollAreaOwnProps>\n>;\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === \"function\") ref(value);\n else if (ref) (ref as MutableRefObject<T | null>).current = value;\n}\n\n/**\n * Themed auto-hide scroll container. Thumb color follows the nearest\n * `[data-theme=\"...\"]` ancestor; pass `theme` to override locally.\n */\nexport const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(\n ({ theme, axis = \"y\", className, children, ...rest }, externalRef) => {\n const innerRef = useRef<HTMLDivElement | null>(null);\n\n const setRef = useCallback(\n (node: HTMLDivElement | null) => {\n innerRef.current = node;\n assignRef(externalRef, node);\n },\n [externalRef],\n );\n\n useEffect(() => {\n const element = innerRef.current;\n if (!element) return;\n\n let hideTimer: number | null = null;\n\n const clearHideTimer = () => {\n if (hideTimer !== null) {\n window.clearTimeout(hideTimer);\n hideTimer = null;\n }\n };\n\n const show = () => {\n element.dataset.scrollbarActive = \"true\";\n clearHideTimer();\n };\n\n const hide = () => {\n element.dataset.scrollbarActive = \"false\";\n clearHideTimer();\n };\n\n const scheduleHide = () => {\n clearHideTimer();\n hideTimer = window.setTimeout(hide, 2000);\n };\n\n const handleInteraction = () => {\n show();\n scheduleHide();\n };\n\n hide();\n\n element.addEventListener(\"scroll\", handleInteraction, {\n passive: true,\n });\n element.addEventListener(\"wheel\", handleInteraction, {\n passive: true,\n });\n element.addEventListener(\"touchstart\", handleInteraction, {\n passive: true,\n });\n element.addEventListener(\"pointerdown\", handleInteraction);\n element.addEventListener(\"keydown\", handleInteraction);\n element.addEventListener(\"focusin\", handleInteraction);\n element.addEventListener(\"focusout\", scheduleHide);\n\n return () => {\n clearHideTimer();\n element.removeEventListener(\"scroll\", handleInteraction);\n element.removeEventListener(\"wheel\", handleInteraction);\n element.removeEventListener(\"touchstart\", handleInteraction);\n element.removeEventListener(\"pointerdown\", handleInteraction);\n element.removeEventListener(\"keydown\", handleInteraction);\n element.removeEventListener(\"focusin\", handleInteraction);\n element.removeEventListener(\"focusout\", scheduleHide);\n };\n }, []);\n\n return (\n <div\n {...rest}\n ref={setRef}\n data-theme={theme}\n className={cn(\n \"polli-scrollbar-subtle polli:min-w-0 polli:max-w-full\",\n axisClasses[axis],\n className,\n )}\n >\n {children}\n </div>\n );\n },\n);\n\nScrollArea.displayName = \"ScrollArea\";\n","import type { FC, ReactNode } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\nimport type { ThemeName } from \"../theme.ts\";\n\nexport type TabButtonProps = {\n active: boolean;\n onClick: () => void;\n children: ReactNode;\n /** Optional cascade override; defaults to inherited [data-theme]. */\n theme?: ThemeName;\n size?: \"md\" | \"sm\";\n variant?: \"default\" | \"ghost\";\n ariaLabel?: string;\n disabled?: boolean;\n className?: string;\n};\n\nconst sizeClasses = {\n md: \"polli:px-4 polli:py-1.5 polli:text-base\",\n sm: \"polli:px-3 polli:py-1.5 polli:text-sm\",\n} as const;\n\nconst variantClasses = {\n default: {\n base: \"polli:border\",\n active: \"polli:bg-theme-bg-active polli:text-theme-text-strong polli:border-theme-border\",\n inactive:\n \"polli:bg-theme-bg-subtle polli:text-theme-text-base polli:border-theme-border polli:hover:bg-theme-bg-active\",\n },\n ghost: {\n base: \"polli:border polli:border-transparent\",\n active: \"polli:bg-theme-bg-active polli:text-theme-text-strong\",\n inactive:\n \"polli:bg-transparent polli:text-theme-text-base polli:hover:bg-theme-bg-subtle polli:hover:text-theme-text-strong\",\n },\n} as const;\n\nexport const TabButton: FC<TabButtonProps> = ({\n theme,\n active,\n onClick,\n children,\n size = \"md\",\n variant = \"default\",\n ariaLabel,\n disabled = false,\n className,\n}) => {\n const classes = variantClasses[variant];\n return (\n <button\n type=\"button\"\n data-theme={theme}\n onClick={onClick}\n aria-label={ariaLabel}\n aria-pressed={active}\n disabled={disabled}\n className={cn(\n \"polli-control polli:inline-flex polli:items-center polli:justify-center polli:rounded-full polli:font-medium polli:leading-normal polli:transition-all polli:duration-200\",\n classes.base,\n active ? classes.active : classes.inactive,\n disabled && \"polli:cursor-not-allowed polli:opacity-50\",\n sizeClasses[size],\n className,\n )}\n >\n {children}\n </button>\n );\n};\n","/** Pollinations model modality to package theme mapping. */\nimport type { ModelCategory } from \"@pollinations/sdk\";\nimport type { ThemeName } from \"../../theme.ts\";\n\n/** A model modality is just a model category. Single source of truth: the SDK. */\nexport type Modality = ModelCategory;\n\nexport const MODALITY_THEMES: Record<ModelCategory, ThemeName> = {\n text: \"blue\",\n image: \"pink\",\n video: \"teal\",\n audio: \"amber\",\n realtime: \"coral\",\n embedding: \"violet\",\n};\n\n/** Theme for a known model category. Total — every category has a theme. */\nexport function modalityTheme(category: ModelCategory): ThemeName {\n return MODALITY_THEMES[category];\n}\n\n/** Map a human-typed category string to the canonical `ModelCategory` key. */\nfunction normalize(category: string): ModelCategory | null {\n const lower = category.toLowerCase();\n if (lower === \"images\") return \"image\";\n if (lower === \"embeddings\") return \"embedding\";\n return lower in MODALITY_THEMES ? (lower as ModelCategory) : null;\n}\n\n/** Theme for an untrusted category string (e.g. user input); null if unknown. */\nexport function getModalityTheme(category: string): ThemeName | null {\n const key = normalize(category);\n return key ? MODALITY_THEMES[key] : null;\n}\n","import type { ModelCategory } from \"@pollinations/sdk\";\nimport { cn } from \"../../lib/cn.ts\";\nimport { Button } from \"../../primitives/Button.tsx\";\nimport { ChevronIcon } from \"../../primitives/ChevronIcon.tsx\";\nimport { Chip } from \"../../primitives/Chip.tsx\";\nimport { Dropdown } from \"../../primitives/Dropdown.tsx\";\nimport { ScrollArea } from \"../../primitives/ScrollArea.tsx\";\nimport { TabButton } from \"../../primitives/TabButton.tsx\";\nimport { modalityTheme } from \"./themes.ts\";\n\nexport type ModelSelectorCategory = ModelCategory;\n\nexport type ModelSelectorItem = {\n id: string;\n name: string;\n title: string;\n description?: string;\n category: ModelSelectorCategory;\n paidOnly?: boolean;\n};\n\nexport type ModelSelectorProps = {\n models: readonly ModelSelectorItem[];\n category: ModelSelectorCategory;\n value: string;\n isLoading?: boolean;\n onChange: (modelId: string) => void;\n};\n\nexport const CATEGORY_LABELS: Record<ModelSelectorCategory, string> = {\n image: \"Image\",\n video: \"Video\",\n text: \"Text\",\n audio: \"Audio\",\n embedding: \"Embeddings\",\n realtime: \"Realtime\",\n};\n\n/** Human-readable label for a model category, e.g. \"embedding\" -> \"Embeddings\". */\nexport function categoryLabel(category: ModelSelectorCategory): string {\n return CATEGORY_LABELS[category];\n}\n\nfunction displayModelName(model: ModelSelectorItem): string {\n return model.title;\n}\n\nexport function ModelSelector({\n models,\n category,\n value,\n isLoading = false,\n onChange,\n}: ModelSelectorProps) {\n const filteredModels = models.filter(\n (model) => model.category === category,\n );\n const currentModel = models.find((model) => model.id === value);\n const theme = modalityTheme(category);\n const modelLabel = currentModel ? displayModelName(currentModel) : \"Select\";\n const accessibleLabel = currentModel\n ? `${CATEGORY_LABELS[category]} model: ${modelLabel}`\n : `Select ${CATEGORY_LABELS[category].toLowerCase()} model`;\n\n return (\n <Dropdown\n theme={theme}\n align=\"end\"\n className=\"polli:w-[min(24rem,calc(100vw-2rem))] polli:p-2\"\n trigger={(open) => (\n <Button\n type=\"button\"\n theme={theme}\n aria-label={accessibleLabel}\n className=\"polli:min-w-64 polli:max-w-full polli:self-start polli:justify-between polli:gap-2\"\n >\n <span className=\"polli:truncate\">{modelLabel}</span>\n <ChevronIcon expanded={open} />\n </Button>\n )}\n >\n {(close) =>\n isLoading ? (\n <p className=\"polli:m-0 polli:px-2 polli:py-2 polli:text-sm polli:text-theme-text-soft\">\n Loading models...\n </p>\n ) : (\n <ScrollArea\n theme={theme}\n className=\"polli:max-h-64 polli:pr-2\"\n >\n <div className=\"polli:flex polli:flex-col polli:gap-1\">\n {filteredModels.map((model) => {\n const isActive = value === model.id;\n return (\n <TabButton\n key={model.id}\n active={isActive}\n theme={modalityTheme(model.category)}\n size=\"sm\"\n variant=\"ghost\"\n className=\"polli:w-full polli:justify-between polli:text-left\"\n onClick={() => {\n onChange(model.id);\n close();\n }}\n >\n <span className=\"polli:truncate\">\n {displayModelName(model)}\n </span>\n {model.paidOnly && (\n <Chip\n size=\"sm\"\n className={cn(\n \"polli:shrink-0\",\n isActive &&\n \"polli:bg-surface-white\",\n )}\n >\n paid\n </Chip>\n )}\n </TabButton>\n );\n })}\n </div>\n </ScrollArea>\n )\n }\n </Dropdown>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","intentClasses","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/modules/gen/index.ts","../../src/lib/cn.ts","../../src/primitives/Chip.tsx","../../src/modules/gen/themes.ts","../../src/modules/gen/ModalityChip.tsx","../../src/modules/gen/ModalityDot.tsx","../../src/primitives/TabButton.tsx","../../src/modules/gen/ModalityTab.tsx","../../src/primitives/icons/index.tsx","../../src/modules/gen/ModelAccessIcon.tsx","../../src/primitives/Button.tsx","../../src/primitives/ChevronIcon.tsx","../../src/primitives/Dropdown.tsx","../../src/primitives/ScrollArea.tsx","../../src/modules/gen/ModelSelector.tsx"],"sourcesContent":["export { ModalityChip } from \"./ModalityChip.tsx\";\nexport { ModalityDot } from \"./ModalityDot.tsx\";\nexport { ModalityTab } from \"./ModalityTab.tsx\";\nexport {\n ModelAccessIcon,\n type ModelAccessIconProps,\n} from \"./ModelAccessIcon.tsx\";\nexport {\n categoryLabel,\n ModelSelector,\n type ModelSelectorCategory,\n} from \"./ModelSelector.tsx\";\nexport {\n getModalityKey,\n modalityBgVar,\n modalityTextColor,\n} from \"./themes.ts\";\n","import { clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({ prefix: \"polli\" });\n\n/** Internal class merge for polli:-prefixed package primitive classes. */\nexport function cn(...inputs: (string | undefined | null | false)[]) {\n return twMerge(clsx(...inputs));\n}\n","import type { ComponentPropsWithoutRef, FC } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\n/** Semantic chip labels for status and metadata badges. */\ntype ChipIntent =\n | \"news\"\n | \"new\"\n | \"free\"\n | \"alpha\"\n | \"neutral\"\n | \"success\"\n | \"warning\"\n | \"info\"\n | \"danger\";\n\nconst chipSizes = {\n icon: \"polli:h-5 polli:w-5 polli:p-0 polli:text-xs\",\n sm: \"polli:h-5 polli:px-2 polli:text-xs\",\n md: \"polli:h-6 polli:px-2.5 polli:text-sm\",\n lg: \"polli:h-7 polli:px-3 polli:text-sm\",\n} as const;\n\nconst intentClasses: Record<ChipIntent, string> = {\n news: \"polli:bg-intent-news-bg-light polli:text-intent-news-text\",\n new: \"polli:bg-intent-new-bg-light polli:text-intent-new-text\",\n free: \"polli:bg-intent-free-bg-light polli:text-intent-free-text\",\n alpha: \"polli:bg-intent-alpha-bg-light polli:text-intent-alpha-text\",\n neutral: \"polli:bg-ink-100/80 polli:text-ink-900\",\n success:\n \"polli:bg-intent-success-bg-bright/15 polli:text-intent-success-text\",\n warning: \"polli:bg-intent-warning-bg-light polli:text-intent-warning-text\",\n info: \"polli:bg-intent-info-bg-light polli:text-intent-info-text\",\n danger: \"polli:bg-intent-danger-bg-light polli:text-intent-danger-text\",\n};\n\nexport type ChipProps = ComponentPropsWithoutRef<\"span\"> & {\n /** Semantic intent. */\n intent?: ChipIntent;\n size?: keyof typeof chipSizes;\n};\n\n// Static, rectangular colored container. Round shapes are reserved for buttons.\n// Reads `bg-theme-bg-active` / `text-theme-text-strong` from the cascade unless\n// `intent` is set (semantic, theme-independent).\nexport const Chip: FC<ChipProps> = ({\n intent,\n size = \"md\",\n className,\n children,\n ...rest\n}) => {\n const intentClass = intent\n ? intentClasses[intent]\n : \"polli:bg-theme-bg-active polli:text-theme-text-strong\";\n return (\n <span\n {...rest}\n className={cn(\n \"polli:inline-flex polli:shrink-0 polli:items-center polli:justify-center polli:gap-1 polli:rounded-lg polli:font-medium polli:leading-none\",\n chipSizes[size],\n intentClass,\n className,\n )}\n >\n {children}\n </span>\n );\n};\n","/** Pollinations model-modality colors. Single-accent model: modality is NOT a\n * page theme — each modality is a fixed color from the `--polli-color-modality-*`\n * tokens, applied as a dot/badge via inline style (the key is dynamic, so a CSS\n * var beats a Tailwind class). The token VALUES live in styles/tokens.css. */\nimport type { ModelCategory } from \"@pollinations/sdk\";\n\n/** The canonical modality keys (match the `--polli-color-modality-*` tokens). */\ntype ModalityKey = ModelCategory;\n\nconst MODALITIES: readonly ModelCategory[] = [\n \"text\",\n \"image\",\n \"video\",\n \"audio\",\n \"realtime\",\n \"embedding\",\n];\n\n/** Map an untrusted category string to its modality key; null if unknown. */\nexport function getModalityKey(category: string): ModalityKey | null {\n const lower = category.toLowerCase();\n if (lower === \"images\") return \"image\";\n if (lower === \"embeddings\") return \"embedding\";\n return MODALITIES.includes(lower as ModelCategory)\n ? (lower as ModelCategory)\n : null;\n}\n\n/** CSS var for a modality's solid color (dot / border / icon). */\nexport const modalityColorVar = (key: ModalityKey): string =>\n `var(--polli-color-modality-${key})`;\n\n/** CSS var for a modality's faint chip background. */\nexport const modalityBgVar = (key: ModalityKey): string =>\n `var(--polli-color-modality-${key}-bg)`;\n\n/** A modality's text color, mixed toward the strong text so it stays readable on its background. */\nexport const modalityTextColor = (key: ModalityKey): string =>\n `color-mix(in oklab, ${modalityColorVar(key)} 72%, var(--polli-color-text-strong))`;\n","import type { ReactNode } from \"react\";\nimport { Chip } from \"../../primitives/Chip.tsx\";\nimport { getModalityKey, modalityBgVar, modalityTextColor } from \"./themes.ts\";\n\n/**\n * A `Chip` colored as a model modality: faint modality background + a readable\n * modality-tinted label. The single-accent way to say \"this is the <text/image/…>\n * modality\" without theming the surrounding chrome.\n *\n * Colors come only from the `--polli-color-modality-*` tokens (via the helpers);\n * the label is blended toward `text-strong` so it stays readable on the faint\n * background in both light and dark. Unknown category → a plain neutral chip.\n */\nexport function ModalityChip({\n modality,\n size = \"sm\",\n className,\n children,\n}: {\n modality: string;\n size?: \"sm\" | \"md\" | \"lg\";\n className?: string;\n children: ReactNode;\n}) {\n const key = getModalityKey(modality);\n if (!key) {\n return (\n <Chip intent=\"neutral\" size={size} className={className}>\n {children}\n </Chip>\n );\n }\n return (\n <Chip\n size={size}\n className={className}\n style={{\n backgroundColor: modalityBgVar(key),\n color: modalityTextColor(key),\n }}\n >\n {children}\n </Chip>\n );\n}\n","import { cn } from \"../../lib/cn.ts\";\nimport { getModalityKey, modalityColorVar } from \"./themes.ts\";\n\n/**\n * A small solid dot in a modality's fixed color. The single-accent way to mark\n * a model's modality (text/image/video/audio/realtime/embedding) without\n * theming the whole control. Renders nothing for an unknown category.\n */\nexport function ModalityDot({\n modality,\n className,\n}: {\n modality: string;\n className?: string;\n}) {\n const key = getModalityKey(modality);\n if (!key) return null;\n return (\n <span\n aria-hidden=\"true\"\n className={cn(\n \"polli:inline-block polli:size-2 polli:shrink-0 polli:rounded-full\",\n className,\n )}\n style={{ backgroundColor: modalityColorVar(key) }}\n />\n );\n}\n","import type {\n ComponentPropsWithoutRef,\n ElementType,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\ntype TabButtonOwnProps = {\n /** `mixed`: a group toggle where only some of its items are selected. */\n active: boolean | \"mixed\";\n /** Omit when rendering as a link (`as`) and navigation carries the change. */\n onClick?: () => void;\n children: ReactNode;\n size?: \"lg\" | \"md\" | \"sm\" | \"xs\";\n variant?: \"soft\" | \"ghost\";\n /**\n * `amber` and `green` fill the selected pill with the wallet's pale paid\n * and Quest Pollen chip colours, which read at the same strength together.\n */\n intent?: \"neutral\" | \"amber\" | \"green\";\n /** Muted secondary text after the label, e.g. a model's creator. */\n detail?: ReactNode;\n ariaLabel?: string;\n disabled?: boolean;\n className?: string;\n};\n\n/**\n * Polymorphic like Button, so a tab that navigates can render a real anchor\n * instead of a click handler — middle-click, right-click and crawlers all\n * depend on that. Defaults to <button>, so existing call sites are unchanged.\n */\nexport type TabButtonProps<T extends ElementType = \"button\"> =\n TabButtonOwnProps & { as?: T } & Omit<\n ComponentPropsWithoutRef<T>,\n keyof TabButtonOwnProps | \"as\"\n >;\n\n/**\n * Shared pill shape (no colors) — used by every TabButton variant.\n *\n * `transition-colors`, not `transition-all`: only background and text colour\n * change between the variants, and `all` additionally animates any layout\n * property that happens to differ — which is the usual cause of a control\n * jittering under the pointer on hover.\n */\nconst tabButtonBaseClass =\n \"polli-control polli:inline-flex polli:items-center polli:justify-center polli:rounded-full polli:font-medium polli:leading-normal polli:transition-colors polli:duration-200\";\n\nconst tabButtonSizeClass = {\n xs: \"polli:px-2.5 polli:py-1 polli:text-xs\",\n lg: \"polli:px-5 polli:py-2 polli:text-lg\",\n md: \"polli:px-4 polli:py-1.5 polli:text-base\",\n sm: \"polli:px-3 polli:py-1.5 polli:text-sm\",\n} as const;\n\nconst variantClasses = {\n // The default tab look: borderless and monochrome. Selected uses `bg-active`\n // — the same light resting fill as the site's normal buttons. Both states\n // deepen to `bg-hover` and use the theme's hover label color. Non-selected\n // uses the quiet `bg-subtle` token until then.\n soft: {\n base: \"\",\n active: \"polli:bg-theme-bg-active polli:text-theme-text-strong polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover\",\n mixed: \"polli:bg-theme-bg-active/45 polli:text-theme-text-strong polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover\",\n inactive:\n \"polli:bg-theme-bg-subtle polli:text-theme-text-base polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover\",\n },\n // Transparent until hovered or selected — for multi-select toggles and\n // inline rows where a filled idle pill would read as a hard selection.\n ghost: {\n base: \"polli:border polli:border-transparent\",\n active: \"polli:bg-theme-bg-active polli:text-theme-text-strong polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover\",\n mixed: \"polli:bg-theme-bg-active/45 polli:text-theme-text-strong polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover\",\n inactive:\n \"polli:bg-transparent polli:text-theme-text-base polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover\",\n },\n} as const;\n\n// Wallet chip pairs: selected fills with the pale tint and deep label; hover\n// previews that fill. Unselected green keeps its green label so it stays\n// recognisable next to amber; unselected amber keeps the neutral label.\nconst intentClasses = {\n amber: {\n active: \"polli:bg-paid-pale polli:text-paid-deep\",\n inactive:\n \"polli:bg-transparent polli:text-theme-text-base polli:hover:bg-paid-pale/60\",\n },\n green: {\n active: \"polli:bg-tier-pale polli:text-tier-deep\",\n inactive:\n \"polli:bg-transparent polli:text-tier-deep polli:hover:bg-tier-pale/60\",\n },\n} as const;\n\nexport function TabButton<T extends ElementType = \"button\">({\n as,\n active,\n onClick,\n children,\n size = \"md\",\n variant = \"soft\",\n intent,\n detail,\n ariaLabel,\n disabled = false,\n className,\n ...rest\n}: TabButtonProps<T>) {\n const Component: ElementType = as || \"button\";\n const isButton = Component === \"button\";\n const colors =\n intent === \"neutral\"\n ? variantClasses.soft\n : intent\n ? {\n ...intentClasses[intent],\n mixed: intentClasses[intent].active,\n }\n : variantClasses[variant];\n const handleClick = disabled\n ? (event: ReactMouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n }\n : onClick;\n\n return (\n <Component\n {...rest}\n {...(isButton ? { type: \"button\", disabled } : {})}\n {...(!isButton && disabled\n ? { \"aria-disabled\": true, tabIndex: -1 }\n : {})}\n {...(Component === \"a\" && disabled ? { href: undefined } : {})}\n onClick={handleClick}\n {...(ariaLabel !== undefined ? { \"aria-label\": ariaLabel } : {})}\n // aria-pressed is for toggles; a link that navigates announces its\n // selected state with aria-current instead.\n {...(isButton\n ? { \"aria-pressed\": active }\n : { \"aria-current\": active === true ? \"page\" : undefined })}\n className={cn(\n tabButtonBaseClass,\n variantClasses[variant].base,\n active === \"mixed\"\n ? colors.mixed\n : active\n ? colors.active\n : colors.inactive,\n disabled &&\n \"polli:pointer-events-none polli:cursor-not-allowed polli:opacity-50\",\n tabButtonSizeClass[size],\n className,\n )}\n >\n {children}\n {detail != null && (\n <span className=\"polli:ml-1 polli:font-normal polli:opacity-70\">\n {detail}\n </span>\n )}\n </Component>\n );\n}\n","import type { ComponentPropsWithoutRef, ReactNode } from \"react\";\nimport { TabButton } from \"../../primitives/TabButton.tsx\";\n\ntype ModalityTabOwnProps = {\n active: boolean;\n onClick: () => void;\n children: ReactNode;\n size?: \"sm\" | \"md\" | \"lg\";\n disabled?: boolean;\n className?: string;\n};\n\n/**\n * A model-filter tab. Monochrome and borderless — it renders the app's `soft`\n * TabButton, so it uses exactly the same tokens as the dashboard tabs: selected\n * is `bg-active` (the resting button fill, no hover), idle is the quiet\n * `bg-subtle`, and hover darkens to `bg-hover` like any button.\n *\n * Rest props (including a runtime-injected ref) pass through to the button —\n * required when the tab is a popover trigger, where the positioner's anchor\n * ref arrives via asChild and a closed prop set would silently drop it.\n */\nexport function ModalityTab({\n active,\n onClick,\n children,\n size = \"md\",\n disabled = false,\n className,\n ...rest\n}: ModalityTabOwnProps &\n Omit<ComponentPropsWithoutRef<\"button\">, keyof ModalityTabOwnProps>) {\n return (\n <TabButton\n {...rest}\n active={active}\n onClick={onClick}\n size={size}\n disabled={disabled}\n className={className}\n >\n {children}\n </TabButton>\n );\n}\n","import type { IconProps } from \"./types.ts\";\n\nconst strokeProps = {\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n} as const;\n\nexport function AppIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"2\" y=\"4\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M2 20h20\" />\n </svg>\n );\n}\n\nexport function AccountIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"12\" cy=\"8\" r=\"4\" />\n <path d=\"M4 21a8 8 0 0 1 16 0\" />\n </svg>\n );\n}\n\nexport function ArrowRightIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M5 12h14M13 6l6 6-6 6\" />\n </svg>\n );\n}\n\nexport function BeakerIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M9 3h6\" />\n <path d=\"M10 3v6.5L4.5 18a2 2 0 0 0 1.7 3h11.6a2 2 0 0 0 1.7-3L14 9.5V3\" />\n <path d=\"M7 14h10\" />\n </svg>\n );\n}\n\nexport function BotIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M12 8V4H8\" />\n <rect x=\"4\" y=\"8\" width=\"16\" height=\"12\" rx=\"2\" />\n <path d=\"M2 14h2M20 14h2\" />\n <path d=\"M9 13v2M15 13v2\" />\n </svg>\n );\n}\n\nexport function BookIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M4 4h5a3 3 0 0 1 3 3v13a2 2 0 0 0-2-2H4z\" />\n <path d=\"M20 4h-5a3 3 0 0 0-3 3v13a2 2 0 0 1 2-2h6z\" />\n </svg>\n );\n}\n\nexport function BugIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"m8 2 1.9 1.9M14.1 3.9 16 2\" />\n <path d=\"M9 7V6a3 3 0 0 1 6 0v1\" />\n <path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a6 6 0 0 1 12 0v3c0 3.3-2.7 6-6 6Z\" />\n <path d=\"M12 20v-9M6.5 9C4.6 8.8 3 7.1 3 5M6 13H2M3 21c0-2.1 1.7-3.9 3.8-4M17.5 9C19.4 8.8 21 7.1 21 5M18 13h4M21 21c0-2.1-1.7-3.9-3.8-4\" />\n </svg>\n );\n}\n\nexport function CheckIcon(props: IconProps) {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n {...strokeProps}\n strokeWidth={2.5}\n {...props}\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\nexport function ClipboardIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n );\n}\n\nexport function ClockIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 7v5l3 2\" />\n </svg>\n );\n}\n\nexport function DiscordIcon(props: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" {...props}>\n <path\n fill=\"currentColor\"\n d=\"M20.32 4.37A19.8 19.8 0 0 0 15.36 2.83a.07.07 0 0 0-.08.04c-.21.38-.45.88-.62 1.27a18.27 18.27 0 0 0-5.52 0 12.84 12.84 0 0 0-.63-1.27.08.08 0 0 0-.08-.04A19.74 19.74 0 0 0 3.47 4.37a.07.07 0 0 0-.03.03C.31 9.07-.55 13.61-.13 18.1a.08.08 0 0 0 .03.06 19.9 19.9 0 0 0 6.08 3.07.08.08 0 0 0 .09-.03c.47-.64.88-1.31 1.24-2.02a.08.08 0 0 0-.04-.1 13.08 13.08 0 0 1-1.9-.91.08.08 0 0 1-.01-.13c.13-.1.25-.2.37-.29a.07.07 0 0 1 .08-.01 14.24 14.24 0 0 0 12.38 0 .07.07 0 0 1 .08.01c.12.1.25.2.38.3a.08.08 0 0 1-.01.12 12.22 12.22 0 0 1-1.9.9.08.08 0 0 0-.04.11c.36.7.77 1.38 1.23 2.02a.08.08 0 0 0 .1.03 19.84 19.84 0 0 0 6.08-3.07.08.08 0 0 0 .03-.05c.5-5.2-.84-9.7-3.77-13.71a.06.06 0 0 0-.03-.03ZM8.02 15.37c-1.18 0-2.16-1.08-2.16-2.4 0-1.32.96-2.4 2.16-2.4 1.2 0 2.18 1.09 2.16 2.4 0 1.32-.96 2.4-2.16 2.4Zm7.96 0c-1.18 0-2.16-1.08-2.16-2.4 0-1.32.96-2.4 2.16-2.4 1.2 0 2.18 1.09 2.16 2.4 0 1.32-.95 2.4-2.16 2.4Z\"\n />\n </svg>\n );\n}\n\nexport function InstagramIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\" />\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <circle\n cx=\"17.5\"\n cy=\"6.5\"\n r=\"1\"\n fill=\"currentColor\"\n stroke=\"none\"\n />\n </svg>\n );\n}\n\nexport function XSocialIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...props}>\n <path\n fill=\"currentColor\"\n d=\"M18.24 2.25h3.31l-7.23 8.26 8.51 11.24h-6.66l-5.21-6.82-5.97 6.82H1.68l7.73-8.84L1.25 2.25h6.83l4.71 6.23 5.45-6.23Zm-1.16 17.52h1.83L7.08 4.13H5.12l11.96 15.64Z\"\n />\n </svg>\n );\n}\n\nexport function LinkedInIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...props}>\n <path\n fill=\"currentColor\"\n d=\"M20.45 20.45h-3.56v-5.57c0-1.33-.03-3.04-1.85-3.04-1.86 0-2.14 1.45-2.14 2.94v5.67H9.34V9h3.42v1.56h.05c.47-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28ZM5.32 7.43a2.07 2.07 0 1 1 0-4.13 2.07 2.07 0 0 1 0 4.13ZM7.1 20.45H3.54V9H7.1v11.45Z\"\n />\n </svg>\n );\n}\n\nexport function DownloadIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" />\n </svg>\n );\n}\n\nexport function ExpandIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M8 3H3v5M16 3h5v5M21 16v5h-5M3 16v5h5\" />\n </svg>\n );\n}\n\nexport function PlayIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...props}>\n <path fill=\"currentColor\" d=\"M7 4.5v15l12-7.5z\" />\n </svg>\n );\n}\n\nexport function PauseIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...props}>\n <path fill=\"currentColor\" d=\"M6 4h4v16H6zM14 4h4v16h-4z\" />\n </svg>\n );\n}\n\nexport function CloudUploadIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M17.5 19H19a4 4 0 0 0 .4-8A7.5 7.5 0 0 0 5 9.5 5 5 0 0 0 6 19h1.5\" />\n <path d=\"m8.5 15.5 3.5-3.5 3.5 3.5M12 12v9\" />\n </svg>\n );\n}\n\nexport function ExternalLinkIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M7 17 17 7M9 7h8v8\" />\n </svg>\n );\n}\n\nexport function GenApiIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"m8 3 4 1.5L16 3v18l-4-1.5L8 21z\" />\n <path d=\"M8 3v18M16 3v18\" />\n </svg>\n );\n}\n\nexport function GitBranchIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <line x1=\"6\" y1=\"3\" x2=\"6\" y2=\"15\" />\n <circle cx=\"18\" cy=\"6\" r=\"3\" />\n <circle cx=\"6\" cy=\"18\" r=\"3\" />\n <path d=\"M18 9a9 9 0 0 1-9 9\" />\n </svg>\n );\n}\n\nexport function GitPullRequestIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"6\" cy=\"6\" r=\"3\" />\n <circle cx=\"18\" cy=\"18\" r=\"3\" />\n <path d=\"M6 9v12M13 6h3a2 2 0 0 1 2 2v7\" />\n </svg>\n );\n}\n\nexport function GraduationCapIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M21.42 10.92a1 1 0 0 0-.02-1.84L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.83l8.57 3.91a2 2 0 0 0 1.66 0z\" />\n <path d=\"M22 10v6\" />\n <path d=\"M6 12.5V16a6 3 0 0 0 12 0v-3.5\" />\n </svg>\n );\n}\n\nexport function SparklesIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M12 3l1.9 5.8a2 2 0 0 0 1.3 1.3L21 12l-5.8 1.9a2 2 0 0 0-1.3 1.3L12 21l-1.9-5.8a2 2 0 0 0-1.3-1.3L3 12l5.8-1.9a2 2 0 0 0 1.3-1.3z\" />\n </svg>\n );\n}\n\nexport function GitHubIcon(props: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" {...props}>\n <path\n fill=\"currentColor\"\n d=\"M12 .5A11.5 11.5 0 0 0 8.36 22.9c.58.11.79-.25.79-.56v-2.16c-3.21.7-3.89-1.38-3.89-1.38-.53-1.34-1.29-1.7-1.29-1.7-1.05-.72.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.04 1.76 2.71 1.25 3.37.96.11-.75.4-1.25.74-1.54-2.56-.29-5.26-1.28-5.26-5.7 0-1.26.45-2.29 1.19-3.1-.12-.29-.52-1.46.11-3.06 0 0 .97-.31 3.16 1.18a10.88 10.88 0 0 1 5.76 0c2.19-1.49 3.15-1.18 3.15-1.18.63 1.6.23 2.77.11 3.06.74.81 1.19 1.84 1.19 3.1 0 4.43-2.7 5.4-5.27 5.69.41.36.78 1.06.78 2.14v3.19c0 .31.21.68.8.56A11.5 11.5 0 0 0 12 .5Z\"\n />\n </svg>\n );\n}\n\nexport function ImageIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-5-5L5 21\" />\n </svg>\n );\n}\n\nexport function CubeIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"m21 16-9 5-9-5V8l9-5 9 5z\" />\n <path d=\"m3 8 9 5 9-5\" />\n <path d=\"M12 13v8\" />\n </svg>\n );\n}\n\nexport function AudioIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M9 18V5l12-2v13\" />\n <circle cx=\"6\" cy=\"18\" r=\"3\" />\n <circle cx=\"18\" cy=\"16\" r=\"3\" />\n </svg>\n );\n}\n\nexport function LockIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n );\n}\n\nexport function LogInIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M10 17l5-5-5-5\" />\n <path d=\"M15 12H3\" />\n <path d=\"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4\" />\n </svg>\n );\n}\n\nexport function LogOutIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\" />\n <path d=\"m16 17 5-5-5-5\" />\n <path d=\"M21 12H9\" />\n </svg>\n );\n}\n\nexport function MailIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"m3 7 9 7 9-7\" />\n </svg>\n );\n}\n\nexport function McpIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"2\" y=\"4\" width=\"8\" height=\"16\" rx=\"1.5\" />\n <rect x=\"14\" y=\"4\" width=\"8\" height=\"16\" rx=\"1.5\" />\n <path d=\"M10 12h4\" />\n </svg>\n );\n}\n\nexport function MenuIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M4 7h16M4 12h16M4 17h16\" />\n </svg>\n );\n}\n\nexport function NewspaperIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M5 4h12a1 1 0 0 1 1 1v14a1 1 0 0 0 1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1z\" />\n <path d=\"M18 8h2a1 1 0 0 1 1 1v9a2 2 0 0 1-2 2\" />\n <path d=\"M7 8h7M7 12h7M7 16h4\" />\n </svg>\n );\n}\n\nexport function PlusIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M12 5v14M5 12h14\" />\n </svg>\n );\n}\n\nexport function TerminalIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <polyline points=\"4 8 8 12 4 16\" />\n <line x1=\"12\" y1=\"20\" x2=\"20\" y2=\"20\" />\n </svg>\n );\n}\n\nexport function ToolIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M14.7 6.3a1 1 0 0 0 0 1.4l2.1 2.1a1 1 0 0 0 1.4 0l4-4a6 6 0 0 1-8 8l-8 8a2.8 2.8 0 0 1-4-4l8-8a6 6 0 0 1 8-8l-4 4Z\" />\n </svg>\n );\n}\n\nexport function TargetIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <circle cx=\"12\" cy=\"12\" r=\"5\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.6\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n );\n}\n\nexport function TokensIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M4 9h16M4 15h16M10 3 8 21M16 3l-2 18\" />\n </svg>\n );\n}\n\nexport function UsageIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M4 18a8 8 0 1 1 16 0\" />\n <path d=\"M12 18l3.5-5\" />\n <path d=\"M7 18h10\" />\n <path d=\"M7.5 12.5 6 11\" />\n <path d=\"M16.5 12.5 18 11\" />\n </svg>\n );\n}\n\nexport function WarningIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M10.3 3.1 1.8 17a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.1a2 2 0 0 0-3.4 0Z\" />\n <path d=\"M12 9v4M12 17h.01\" />\n </svg>\n );\n}\n\nexport function TrendUpIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <polyline points=\"3 17 9 11 13 15 21 7\" />\n <polyline points=\"15 7 21 7 21 13\" />\n </svg>\n );\n}\n\nexport function EarningsIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <ellipse cx=\"10\" cy=\"6\" rx=\"6\" ry=\"3\" />\n <path d=\"M4 6v8c0 1.7 2.7 3 6 3 1.2 0 2.4-.2 3.3-.5\" />\n <path d=\"M16 6v4\" />\n <path d=\"M4 10c0 1.7 2.7 3 6 3 1.1 0 2.1-.1 3-.4\" />\n <path d=\"M18 14v6\" />\n <path d=\"M15 17h6\" />\n </svg>\n );\n}\n\nexport function WalletIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M3 7a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2v2H5a2 2 0 0 0-2 2V7Z\" />\n <path d=\"M3 11a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6Z\" />\n <circle cx=\"17\" cy=\"14\" r=\"1.25\" fill=\"currentColor\" />\n </svg>\n );\n}\n\n/** A paper receipt: what a price includes. */\nexport function ReceiptIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M6 3h12v18l-2-1.5-2 1.5-2-1.5-2 1.5-2-1.5L6 21Z\" />\n <path d=\"M9 8h6M9 12h6M9 16h3\" />\n </svg>\n );\n}\n\nexport function MoonIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z\" />\n </svg>\n );\n}\n\nexport function SunIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4\" />\n </svg>\n );\n}\n\nexport function XIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\nexport function TrashIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4h8v2\" />\n <path d=\"m19 6-1 15H6L5 6\" />\n <path d=\"M10 11v6M14 11v6\" />\n </svg>\n );\n}\n\n// --- Model modality / capability / price glyphs (single-ink, replace emoji) ---\n\nexport function ChatIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nexport function RobotIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"3\" y=\"6\" width=\"18\" height=\"15\" rx=\"3\" />\n <path d=\"M12 2v4M3 11H1v5h2M21 11h2v5h-2M8 17h8\" />\n <circle cx=\"9\" cy=\"12\" r=\"1\" />\n <circle cx=\"15\" cy=\"12\" r=\"1\" />\n </svg>\n );\n}\n\nexport function EyeIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n );\n}\n\nexport function EyeOffIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M3 3l18 18\" />\n </svg>\n );\n}\n\nexport function VideoIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"m22 8-6 4 6 4V8z\" />\n <rect x=\"2\" y=\"6\" width=\"14\" height=\"12\" rx=\"2\" />\n </svg>\n );\n}\n\nexport function MicIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <path d=\"M12 19v3\" />\n </svg>\n );\n}\n\nexport function SpeakerIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M11 5 6 9H2v6h4l5 4z\" />\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" />\n <path d=\"M19.07 4.93a10 10 0 0 1 0 14.14\" />\n </svg>\n );\n}\n\nexport function MegaphoneIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"m3 11 18-5v12L3 14v-3z\" />\n <path d=\"m7 15 1.5 6H14l-2-7\" />\n </svg>\n );\n}\n\nexport function ReasoningIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M9 18h6M10 22h4M15.1 14c.2-1 .6-1.7 1.4-2.5A4.6 4.6 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.8.8 1.2 1.5 1.4 2.5\" />\n </svg>\n );\n}\n\nexport function RocketIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z\" />\n <path d=\"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z\" />\n <path d=\"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0\" />\n <path d=\"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5\" />\n </svg>\n );\n}\n\nexport function SearchIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n );\n}\n\nexport function SignOutIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\" />\n <path d=\"m16 17 5-5-5-5\" />\n <path d=\"M21 12H9\" />\n </svg>\n );\n}\n\nexport function PowerIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M12 2v10\" />\n <path d=\"M18.4 6.6a9 9 0 1 1-12.77.04\" />\n </svg>\n );\n}\n\nexport function SparkleIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M9.94 15.5A2 2 0 0 0 8.5 14.06l-6.14-1.58a.5.5 0 0 1 0-.96L8.5 9.94A2 2 0 0 0 9.94 8.5l1.58-6.14a.5.5 0 0 1 .96 0L14.06 8.5A2 2 0 0 0 15.5 9.94l6.14 1.58a.5.5 0 0 1 0 .96L15.5 14.06a2 2 0 0 0-1.44 1.44l-1.58 6.14a.5.5 0 0 1-.96 0z\" />\n <path d=\"M20 3v4\" />\n <path d=\"M22 5h-4\" />\n <path d=\"M4 17v2\" />\n <path d=\"M5 18H3\" />\n </svg>\n );\n}\n\nexport function StarIcon({\n filled = false,\n ...props\n}: IconProps & { filled?: boolean }) {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n {...strokeProps}\n fill={filled ? \"currentColor\" : \"none\"}\n {...props}\n >\n <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\" />\n </svg>\n );\n}\n\nexport function CodeIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"m16 18 6-6-6-6M8 6l-6 6 6 6\" />\n </svg>\n );\n}\n\nexport function DatabaseIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5v14a9 3 0 0 0 18 0V5\" />\n <path d=\"M3 12a9 3 0 0 0 18 0\" />\n </svg>\n );\n}\n\nexport function CardIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <rect x=\"2\" y=\"5\" width=\"20\" height=\"14\" rx=\"2\" />\n <path d=\"M2 10h20\" />\n </svg>\n );\n}\n\nexport function SproutIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M7 20h10\" />\n <path d=\"M10 20c5.5-2.5.8-6.4 3-10\" />\n <path d=\"M9.5 9.4c1.1.8 1.8 2.2 2.3 3.7-2 .4-3.5.4-4.8-.3-1.2-.6-2.3-1.9-3-4.2 2.8-.5 4.4 0 5.5.8z\" />\n <path d=\"M14.1 6a7 7 0 0 0-1.1 4c1.9-.1 3.3-.6 4.3-1.4 1-1 1.6-2.3 1.7-4.6-2.7.1-4 1-4.9 2z\" />\n </svg>\n );\n}\n\nexport function PencilIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z\" />\n </svg>\n );\n}\n\nexport function KeyIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"7.5\" cy=\"15.5\" r=\"5.5\" />\n <path d=\"m21 2-9.6 9.6\" />\n <path d=\"m15.5 7.5 3 3L22 7l-3-3\" />\n </svg>\n );\n}\n\nexport function GlobeIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n );\n}\n\nexport function ArrowLeftIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M19 12H5\" />\n <path d=\"m12 19-7-7 7-7\" />\n </svg>\n );\n}\n\nexport function RefreshIcon(props: IconProps) {\n return (\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" {...strokeProps} {...props}>\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36\" />\n <path d=\"M21 3v6h-6\" />\n </svg>\n );\n}\n\nexport type { IconProps } from \"./types.ts\";\n","import { CardIcon, SproutIcon } from \"../../primitives/icons/index.tsx\";\n\nexport type ModelAccessIconProps = {\n paidOnly?: boolean;\n className?: string;\n};\n\n/** Compact model-access marker shared by every model picker. */\nexport function ModelAccessIcon({\n paidOnly = false,\n className = \"polli:h-3.5 polli:w-3.5\",\n}: ModelAccessIconProps) {\n const label = paidOnly ? \"Paid Pollen required\" : \"Works with any Pollen\";\n const Icon = paidOnly ? CardIcon : SproutIcon;\n\n return (\n <span\n role=\"img\"\n aria-label={label}\n title={label}\n className=\"polli:inline-flex polli:shrink-0\"\n >\n <Icon className={className} />\n </span>\n );\n}\n","import type {\n PropsWithChildren,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\n/** Semantic action roles. Label recipes live on Chip. */\ntype ButtonIntent = \"danger\" | \"info\" | \"neutral\" | \"brand\" | \"commit\";\nexport type ButtonAppearance = \"pill\" | \"raised\";\n\nconst pillSizes = {\n icon: \"polli:h-12 polli:w-12 polli:p-0\",\n xs: \"polli:h-5 polli:px-1.5 polli:py-0 polli:text-[11px] polli:leading-none\",\n sm: \"polli:min-h-7 polli:px-2 polli:pt-0.5 polli:pb-1\",\n md: \"polli:min-h-9 polli:px-4 polli:pt-1.5 polli:pb-2\",\n lg: \"polli:min-h-12 polli:px-6 polli:py-3 polli:text-base\",\n} as const;\n\nconst raisedSizes = {\n icon: \"polli:h-12 polli:w-12 polli:p-0\",\n xs: \"polli:px-2 polli:py-1 polli:text-xs\",\n sm: \"polli:px-4 polli:py-2 polli:text-sm\",\n md: \"polli:px-7 polli:py-3.5 polli:text-base\",\n lg: \"polli:px-8 polli:py-4 polli:text-lg\",\n} as const;\n\nconst appearanceClasses: Record<ButtonAppearance, string> = {\n pill: \"polli:rounded-full\",\n raised:\n \"polli:rounded-xl polli:border-r-[3px] polli:border-b-[3px] polli:border-solid \" +\n \"polli:border-theme-text-strong/20 polli:hover:border-theme-text-strong/45\",\n};\n\n// Cascade-driven base — reads [data-theme] vars.\nconst themeClasses =\n \"polli:bg-theme-bg-active polli:text-theme-text-strong \" +\n \"polli:hover:bg-theme-bg-hover polli:hover:text-theme-text-hover polli:transition-colors\";\n\n// Primary actions have a soft accent fill in light mode and an outline in dark.\n// `brand` keeps a calm hover; `commit` strengthens the fill on hover and focus.\nconst outlined =\n \"polli:border polli:border-theme-text-soft polli:bg-theme-bg-active/30 polli:text-theme-text-strong \" +\n \"polli:transition-colors polli:[.dark_&]:bg-transparent\";\nconst intentClasses: Record<ButtonIntent, string> = {\n brand: `${outlined} polli:hover:bg-theme-bg-active/50 polli:[.dark_&]:hover:bg-theme-text-soft/10`,\n commit:\n `${outlined} polli:hover:border-theme-bg-active polli:hover:bg-theme-bg-active ` +\n \"polli:focus-visible:border-theme-bg-active polli:focus-visible:bg-theme-bg-active \" +\n \"polli:[.dark_&]:hover:bg-theme-bg-active polli:[.dark_&]:focus-visible:bg-theme-bg-active\",\n danger:\n \"polli:border polli:border-intent-danger-text polli:bg-intent-danger-bg-light polli:text-intent-danger-text \" +\n \"polli:hover:bg-intent-danger-bg-hover polli:focus-visible:bg-intent-danger-bg-hover polli:transition-colors \" +\n \"polli:[.dark_&]:bg-transparent polli:[.dark_&]:hover:bg-intent-danger-bg-hover polli:[.dark_&]:focus-visible:bg-intent-danger-bg-hover\",\n info:\n \"polli:bg-intent-info-bg-light polli:text-intent-info-text \" +\n \"polli:hover:bg-intent-info-bg-hover polli:transition-colors\",\n // Secondary actions never take the accent: one step stronger grey on hover.\n neutral:\n \"polli:bg-theme-bg-subtle polli:text-theme-text-base \" +\n \"polli:hover:bg-theme-text-muted/25 polli:hover:text-theme-text-strong polli:transition-colors\",\n};\n\ntype BaseButtonProps = {\n icon?: ReactNode;\n /** Optional semantic recipe; omit for the ambient theme button. */\n intent?: ButtonIntent;\n /** `raised` is the stronger website CTA treatment. */\n appearance?: ButtonAppearance;\n size?: keyof typeof pillSizes;\n className?: string;\n disabled?: boolean;\n};\n\nconst buttonClasses = ({\n intent,\n appearance = \"pill\",\n size,\n className,\n disabled,\n busy,\n}: BaseButtonProps & { disabled?: boolean; busy?: boolean }) => {\n const colorClasses = intent ? intentClasses[intent] : themeClasses;\n const sizeClasses = appearance === \"raised\" ? raisedSizes : pillSizes;\n return cn(\n \"polli-control polli:inline-flex polli:items-center polli:justify-center polli:self-center polli:font-body polli:text-sm polli:font-medium polli:leading-normal polli:box-border polli:border polli:border-transparent\",\n // A busy button is working, not unavailable: it keeps its strength.\n busy\n ? \"polli:cursor-progress\"\n : disabled\n ? \"polli:opacity-50 polli:cursor-not-allowed\"\n : \"polli:hover:filter polli:hover:brightness-105 polli:cursor-pointer\",\n colorClasses,\n appearanceClasses[appearance],\n sizeClasses[size || \"md\"],\n className,\n );\n};\n\nexport type ButtonProps<T extends React.ElementType = \"button\"> =\n PropsWithChildren<BaseButtonProps> & {\n as?: T;\n } & Omit<React.ComponentPropsWithoutRef<T>, keyof BaseButtonProps | \"as\">;\n\nexport function Button<T extends React.ElementType = \"button\">({\n as,\n children,\n icon,\n intent,\n appearance,\n size,\n className,\n disabled,\n ...buttonProps\n}: ButtonProps<T>) {\n const Component: React.ElementType = as || \"button\";\n const isButton = Component === \"button\";\n const isAnchor = Component === \"a\";\n const buttonType = (\n buttonProps as {\n type?: \"button\" | \"submit\" | \"reset\";\n }\n ).type;\n const onClick = (\n buttonProps as {\n onClick?: (event: ReactMouseEvent) => void;\n }\n ).onClick;\n const ariaBusy = (\n buttonProps as { \"aria-busy\"?: boolean | \"true\" | \"false\" }\n )[\"aria-busy\"];\n const busy = ariaBusy === true || ariaBusy === \"true\";\n const handleClick = disabled\n ? (event: ReactMouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n }\n : onClick;\n\n return (\n <Component\n {...buttonProps}\n data-intent={intent}\n {...(isButton ? { type: buttonType ?? \"button\", disabled } : {})}\n {...(!isButton && disabled\n ? { \"aria-disabled\": true, tabIndex: -1 }\n : {})}\n {...(isAnchor && disabled ? { href: undefined } : {})}\n onClick={handleClick}\n className={buttonClasses({\n intent,\n appearance,\n size,\n className,\n disabled,\n busy,\n })}\n >\n {icon && (\n <span\n aria-hidden=\"true\"\n className=\"polli:mr-2 polli:flex polli:size-4 polli:shrink-0 polli:[&>svg]:size-full\"\n >\n {busy ? (\n <span className=\"polli:block polli:size-full polli:animate-spin polli:rounded-full polli:border-2 polli:border-current polli:border-r-transparent polli:motion-reduce:animate-none\" />\n ) : (\n icon\n )}\n </span>\n )}\n {children}\n </Component>\n );\n}\n","import type { FC } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\ntype ChevronIconProps = {\n /** Rotate 180° when expanded/open. */\n expanded?: boolean;\n className?: string;\n};\n\n/**\n * Canonical chevron used for collapsibles, dropdown triggers, and select pills\n * across the app. One icon everywhere.\n */\nexport const ChevronIcon: FC<ChevronIconProps> = ({ expanded, className }) => (\n <svg\n aria-hidden=\"true\"\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n className={cn(\n \"polli:shrink-0 polli:transition-transform polli:duration-200 polli:ease-out\",\n expanded && \"polli:rotate-180\",\n className,\n )}\n >\n <path\n d=\"M3 4.5 6 7.5l3-3\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"1.5\"\n />\n </svg>\n);\n","import { Popover } from \"@ark-ui/react/popover\";\nimport { Portal } from \"@ark-ui/react/portal\";\nimport type { CSSProperties, FC, ReactNode } from \"react\";\nimport { useId, useState } from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\nconst DEFAULT_PANEL = \"polli:rounded-lg polli:bg-surface-menu polli:shadow-lg\";\n\nexport type DropdownProps = {\n /** Trigger element; receives the current open state (e.g. to rotate a chevron). */\n trigger: (open: boolean) => ReactNode;\n /** Panel content. As a function, receives `close` to dismiss after a selection. */\n children: ReactNode | ((close: () => void) => ReactNode);\n /** Panel placement relative to the trigger. */\n align?: \"start\" | \"end\";\n side?: \"top\" | \"bottom\";\n /** Controlled open state. Omit to let the Dropdown manage its own. */\n open?: boolean;\n /** Render inside the parent dialog so its focus and accessibility scope includes the menu. */\n portalled?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Appended after the surface (widths, padding, max-height…). */\n className?: string;\n /** Theme variables for portaled content that cannot inherit from its trigger. */\n panelStyle?: CSSProperties;\n};\n\nexport const Dropdown: FC<DropdownProps> = ({\n trigger,\n children,\n align = \"start\",\n side = \"bottom\",\n open: openProp,\n portalled = true,\n onOpenChange,\n className,\n panelStyle,\n}) => {\n const triggerId = useId();\n const [internalOpen, setInternalOpen] = useState(false);\n const isControlled = openProp !== undefined;\n const open = isControlled ? openProp : internalOpen;\n\n const setOpen = (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n };\n\n return (\n <Popover.Root\n ids={{ trigger: triggerId }}\n open={open}\n portalled={portalled}\n onOpenChange={(details) => setOpen(details.open)}\n positioning={{\n placement: `${side}-${align}`,\n strategy: portalled ? \"absolute\" : \"fixed\",\n }}\n >\n <Popover.Trigger asChild>{trigger(open)}</Popover.Trigger>\n <Portal disabled={!portalled}>\n <Popover.Positioner>\n <Popover.Content\n aria-labelledby={triggerId}\n style={panelStyle}\n className={cn(\n \"polli:z-[120] polli:overflow-hidden polli:focus:outline-none\",\n DEFAULT_PANEL,\n className,\n )}\n >\n {typeof children === \"function\"\n ? children(() => setOpen(false))\n : children}\n </Popover.Content>\n </Popover.Positioner>\n </Portal>\n </Popover.Root>\n );\n};\n","import {\n type ComponentPropsWithoutRef,\n forwardRef,\n type MutableRefObject,\n type PropsWithChildren,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n} from \"react\";\nimport { cn } from \"../lib/cn.ts\";\n\ntype ScrollAxis = \"y\" | \"x\" | \"both\";\n\nconst axisClasses: Record<ScrollAxis, string> = {\n y: \"polli:overflow-y-auto polli:overflow-x-hidden\",\n x: \"polli:overflow-x-auto polli:overflow-y-hidden\",\n both: \"polli:overflow-auto\",\n};\n\ntype ScrollAreaOwnProps = {\n /** Scroll direction. Default `y`. */\n axis?: ScrollAxis;\n /** Use the system scrollbar instead of the custom auto-hiding track. */\n scrollbar?: \"subtle\" | \"native\";\n className?: string;\n};\n\nexport type ScrollAreaProps = PropsWithChildren<\n ScrollAreaOwnProps &\n Omit<ComponentPropsWithoutRef<\"div\">, keyof ScrollAreaOwnProps>\n>;\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === \"function\") ref(value);\n else if (ref) (ref as MutableRefObject<T | null>).current = value;\n}\n\n/** Scroll container with an optional themed, auto-hiding scrollbar. */\nexport const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(\n (\n { axis = \"y\", scrollbar = \"subtle\", className, children, ...rest },\n externalRef,\n ) => {\n const innerRef = useRef<HTMLDivElement | null>(null);\n\n const setRef = useCallback(\n (node: HTMLDivElement | null) => {\n innerRef.current = node;\n assignRef(externalRef, node);\n },\n [externalRef],\n );\n\n useEffect(() => {\n if (scrollbar === \"native\") return;\n const element = innerRef.current;\n if (!element) return;\n\n let hideTimer: number | null = null;\n\n const clearHideTimer = () => {\n if (hideTimer !== null) {\n window.clearTimeout(hideTimer);\n hideTimer = null;\n }\n };\n\n const show = () => {\n element.dataset.scrollbarActive = \"true\";\n clearHideTimer();\n };\n\n const hide = () => {\n element.dataset.scrollbarActive = \"false\";\n clearHideTimer();\n };\n\n const scheduleHide = () => {\n clearHideTimer();\n hideTimer = window.setTimeout(hide, 2000);\n };\n\n const handleInteraction = () => {\n show();\n scheduleHide();\n };\n\n hide();\n\n element.addEventListener(\"scroll\", handleInteraction, {\n passive: true,\n });\n element.addEventListener(\"wheel\", handleInteraction, {\n passive: true,\n });\n element.addEventListener(\"touchstart\", handleInteraction, {\n passive: true,\n });\n element.addEventListener(\"pointerdown\", handleInteraction);\n element.addEventListener(\"keydown\", handleInteraction);\n element.addEventListener(\"focusin\", handleInteraction);\n element.addEventListener(\"focusout\", scheduleHide);\n\n return () => {\n clearHideTimer();\n element.removeEventListener(\"scroll\", handleInteraction);\n element.removeEventListener(\"wheel\", handleInteraction);\n element.removeEventListener(\"touchstart\", handleInteraction);\n element.removeEventListener(\"pointerdown\", handleInteraction);\n element.removeEventListener(\"keydown\", handleInteraction);\n element.removeEventListener(\"focusin\", handleInteraction);\n element.removeEventListener(\"focusout\", scheduleHide);\n };\n }, [scrollbar]);\n\n return (\n <div\n {...rest}\n ref={setRef}\n className={cn(\n scrollbar === \"subtle\" && \"polli-scrollbar-subtle\",\n \"polli:min-w-0 polli:max-w-full\",\n axisClasses[axis],\n className,\n )}\n >\n {children}\n </div>\n );\n },\n);\n\nScrollArea.displayName = \"ScrollArea\";\n","import type { ModelCategory, ModelInfo } from \"@pollinations/sdk\";\nimport { Button } from \"../../primitives/Button.tsx\";\nimport { ChevronIcon } from \"../../primitives/ChevronIcon.tsx\";\nimport { Dropdown } from \"../../primitives/Dropdown.tsx\";\nimport { ScrollArea } from \"../../primitives/ScrollArea.tsx\";\nimport { TabButton } from \"../../primitives/TabButton.tsx\";\nimport { ModelAccessIcon } from \"./ModelAccessIcon.tsx\";\n\nexport type ModelSelectorCategory = ModelCategory;\n\ntype ModelSelectorProps = {\n models: readonly ModelInfo[];\n category: ModelSelectorCategory;\n value: string;\n isLoading?: boolean;\n onChange: (modelId: string) => void;\n};\n\nconst CATEGORY_LABELS: Record<ModelSelectorCategory, string> = {\n image: \"Image\",\n video: \"Video\",\n text: \"Text\",\n audio: \"Audio\",\n \"3d\": \"3D\",\n embedding: \"Embeddings\",\n realtime: \"Realtime\",\n};\n\n/** Human-readable label for a model category, e.g. \"embedding\" -> \"Embeddings\". */\nexport function categoryLabel(category: ModelSelectorCategory): string {\n return CATEGORY_LABELS[category];\n}\n\nfunction modelId(model: ModelInfo): string {\n return model.id ?? model.name;\n}\n\nexport function ModelSelector({\n models,\n category,\n value,\n isLoading = false,\n onChange,\n}: ModelSelectorProps) {\n const filteredModels = models\n .filter((model) => model.category === category)\n .sort((a, b) => modelId(a).localeCompare(modelId(b)));\n const currentModel = models.find((model) => modelId(model) === value);\n const modelLabel = currentModel\n ? (currentModel.title ?? currentModel.name)\n : \"Select\";\n const accessibleLabel = currentModel\n ? `${CATEGORY_LABELS[category]} model: ${modelLabel}`\n : `Select ${CATEGORY_LABELS[category].toLowerCase()} model`;\n\n return (\n <Dropdown\n align=\"start\"\n className=\"polli:w-[min(24rem,calc(100vw-2rem))] polli:p-2\"\n trigger={(open) => (\n <Button\n type=\"button\"\n aria-label={accessibleLabel}\n className=\"polli:min-w-64 polli:max-w-full polli:self-start polli:justify-between polli:gap-2\"\n >\n <span className=\"polli:flex polli:min-w-0 polli:items-center polli:gap-2\">\n <span className=\"polli:truncate\">{modelLabel}</span>\n {currentModel && (\n <ModelAccessIcon\n paidOnly={currentModel.paid_only}\n />\n )}\n </span>\n <ChevronIcon expanded={open} />\n </Button>\n )}\n >\n {(close) =>\n isLoading ? (\n <p className=\"polli:m-0 polli:px-2 polli:py-2 polli:text-sm polli:text-theme-text-soft\">\n Loading models...\n </p>\n ) : (\n <ScrollArea className=\"polli:max-h-80 polli:pr-2\">\n <div className=\"polli:flex polli:flex-col polli:gap-1\">\n {filteredModels.map((model) => {\n const id = modelId(model);\n const isActive = value === id;\n return (\n <TabButton\n key={id}\n active={isActive}\n size=\"sm\"\n variant=\"ghost\"\n className=\"polli:w-full polli:justify-start polli:text-left\"\n onClick={() => {\n onChange(id);\n close();\n }}\n >\n <span className=\"polli:flex polli:min-w-0 polli:flex-col polli:gap-0.5\">\n <span className=\"polli:flex polli:min-w-0 polli:items-center polli:gap-2\">\n <span className=\"polli:truncate\">\n {model.title ?? model.name}\n </span>\n <ModelAccessIcon\n paidOnly={model.paid_only}\n />\n </span>\n {model.description && (\n <span className=\"polli:truncate polli:text-xs polli:font-normal polli:text-theme-text-muted\">\n {model.description}\n </span>\n )}\n </span>\n </TabButton>\n );\n })}\n </div>\n </ScrollArea>\n )\n }\n </Dropdown>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","intentClasses","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","intentClasses","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime"]}
@@ -1,35 +1,84 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ModelCategory } from '@pollinations/sdk';
3
- import { T as ThemeName } from '../theme-jRPPBmld.cjs';
2
+ import { ReactNode, ComponentPropsWithoutRef } from 'react';
3
+ import { ModelInfo, ModelCategory } from '@pollinations/sdk';
4
4
 
5
- type ModelSelectorCategory = ModelCategory;
6
- type ModelSelectorItem = {
7
- id: string;
8
- name: string;
9
- title: string;
10
- description?: string;
11
- category: ModelSelectorCategory;
5
+ /**
6
+ * A `Chip` colored as a model modality: faint modality background + a readable
7
+ * modality-tinted label. The single-accent way to say "this is the <text/image/…>
8
+ * modality" without theming the surrounding chrome.
9
+ *
10
+ * Colors come only from the `--polli-color-modality-*` tokens (via the helpers);
11
+ * the label is blended toward `text-strong` so it stays readable on the faint
12
+ * background in both light and dark. Unknown category → a plain neutral chip.
13
+ */
14
+ declare function ModalityChip({ modality, size, className, children, }: {
15
+ modality: string;
16
+ size?: "sm" | "md" | "lg";
17
+ className?: string;
18
+ children: ReactNode;
19
+ }): react_jsx_runtime.JSX.Element;
20
+
21
+ /**
22
+ * A small solid dot in a modality's fixed color. The single-accent way to mark
23
+ * a model's modality (text/image/video/audio/realtime/embedding) without
24
+ * theming the whole control. Renders nothing for an unknown category.
25
+ */
26
+ declare function ModalityDot({ modality, className, }: {
27
+ modality: string;
28
+ className?: string;
29
+ }): react_jsx_runtime.JSX.Element | null;
30
+
31
+ type ModalityTabOwnProps = {
32
+ active: boolean;
33
+ onClick: () => void;
34
+ children: ReactNode;
35
+ size?: "sm" | "md" | "lg";
36
+ disabled?: boolean;
37
+ className?: string;
38
+ };
39
+ /**
40
+ * A model-filter tab. Monochrome and borderless — it renders the app's `soft`
41
+ * TabButton, so it uses exactly the same tokens as the dashboard tabs: selected
42
+ * is `bg-active` (the resting button fill, no hover), idle is the quiet
43
+ * `bg-subtle`, and hover darkens to `bg-hover` like any button.
44
+ *
45
+ * Rest props (including a runtime-injected ref) pass through to the button —
46
+ * required when the tab is a popover trigger, where the positioner's anchor
47
+ * ref arrives via asChild and a closed prop set would silently drop it.
48
+ */
49
+ declare function ModalityTab({ active, onClick, children, size, disabled, className, ...rest }: ModalityTabOwnProps & Omit<ComponentPropsWithoutRef<"button">, keyof ModalityTabOwnProps>): react_jsx_runtime.JSX.Element;
50
+
51
+ type ModelAccessIconProps = {
12
52
  paidOnly?: boolean;
53
+ className?: string;
13
54
  };
55
+ /** Compact model-access marker shared by every model picker. */
56
+ declare function ModelAccessIcon({ paidOnly, className, }: ModelAccessIconProps): react_jsx_runtime.JSX.Element;
57
+
58
+ type ModelSelectorCategory = ModelCategory;
14
59
  type ModelSelectorProps = {
15
- models: readonly ModelSelectorItem[];
60
+ models: readonly ModelInfo[];
16
61
  category: ModelSelectorCategory;
17
62
  value: string;
18
63
  isLoading?: boolean;
19
64
  onChange: (modelId: string) => void;
20
65
  };
21
- declare const CATEGORY_LABELS: Record<ModelSelectorCategory, string>;
22
66
  /** Human-readable label for a model category, e.g. "embedding" -> "Embeddings". */
23
67
  declare function categoryLabel(category: ModelSelectorCategory): string;
24
68
  declare function ModelSelector({ models, category, value, isLoading, onChange, }: ModelSelectorProps): react_jsx_runtime.JSX.Element;
25
69
 
26
- /** Pollinations model modality to package theme mapping. */
70
+ /** Pollinations model-modality colors. Single-accent model: modality is NOT a
71
+ * page theme — each modality is a fixed color from the `--polli-color-modality-*`
72
+ * tokens, applied as a dot/badge via inline style (the key is dynamic, so a CSS
73
+ * var beats a Tailwind class). The token VALUES live in styles/tokens.css. */
27
74
 
28
- /** A model modality is just a model category. Single source of truth: the SDK. */
29
- type Modality = ModelCategory;
30
- /** Theme for a known model category. Total — every category has a theme. */
31
- declare function modalityTheme(category: ModelCategory): ThemeName;
32
- /** Theme for an untrusted category string (e.g. user input); null if unknown. */
33
- declare function getModalityTheme(category: string): ThemeName | null;
75
+ /** The canonical modality keys (match the `--polli-color-modality-*` tokens). */
76
+ type ModalityKey = ModelCategory;
77
+ /** Map an untrusted category string to its modality key; null if unknown. */
78
+ declare function getModalityKey(category: string): ModalityKey | null;
79
+ /** CSS var for a modality's faint chip background. */
80
+ declare const modalityBgVar: (key: ModalityKey) => string;
81
+ /** A modality's text color, mixed toward the strong text so it stays readable on its background. */
82
+ declare const modalityTextColor: (key: ModalityKey) => string;
34
83
 
35
- export { CATEGORY_LABELS, type Modality, ModelSelector, type ModelSelectorCategory, type ModelSelectorItem, type ModelSelectorProps, categoryLabel, getModalityTheme, modalityTheme };
84
+ export { ModalityChip, ModalityDot, ModalityTab, ModelAccessIcon, type ModelAccessIconProps, ModelSelector, type ModelSelectorCategory, categoryLabel, getModalityKey, modalityBgVar, modalityTextColor };
@@ -1,35 +1,84 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ModelCategory } from '@pollinations/sdk';
3
- import { T as ThemeName } from '../theme-jRPPBmld.js';
2
+ import { ReactNode, ComponentPropsWithoutRef } from 'react';
3
+ import { ModelInfo, ModelCategory } from '@pollinations/sdk';
4
4
 
5
- type ModelSelectorCategory = ModelCategory;
6
- type ModelSelectorItem = {
7
- id: string;
8
- name: string;
9
- title: string;
10
- description?: string;
11
- category: ModelSelectorCategory;
5
+ /**
6
+ * A `Chip` colored as a model modality: faint modality background + a readable
7
+ * modality-tinted label. The single-accent way to say "this is the <text/image/…>
8
+ * modality" without theming the surrounding chrome.
9
+ *
10
+ * Colors come only from the `--polli-color-modality-*` tokens (via the helpers);
11
+ * the label is blended toward `text-strong` so it stays readable on the faint
12
+ * background in both light and dark. Unknown category → a plain neutral chip.
13
+ */
14
+ declare function ModalityChip({ modality, size, className, children, }: {
15
+ modality: string;
16
+ size?: "sm" | "md" | "lg";
17
+ className?: string;
18
+ children: ReactNode;
19
+ }): react_jsx_runtime.JSX.Element;
20
+
21
+ /**
22
+ * A small solid dot in a modality's fixed color. The single-accent way to mark
23
+ * a model's modality (text/image/video/audio/realtime/embedding) without
24
+ * theming the whole control. Renders nothing for an unknown category.
25
+ */
26
+ declare function ModalityDot({ modality, className, }: {
27
+ modality: string;
28
+ className?: string;
29
+ }): react_jsx_runtime.JSX.Element | null;
30
+
31
+ type ModalityTabOwnProps = {
32
+ active: boolean;
33
+ onClick: () => void;
34
+ children: ReactNode;
35
+ size?: "sm" | "md" | "lg";
36
+ disabled?: boolean;
37
+ className?: string;
38
+ };
39
+ /**
40
+ * A model-filter tab. Monochrome and borderless — it renders the app's `soft`
41
+ * TabButton, so it uses exactly the same tokens as the dashboard tabs: selected
42
+ * is `bg-active` (the resting button fill, no hover), idle is the quiet
43
+ * `bg-subtle`, and hover darkens to `bg-hover` like any button.
44
+ *
45
+ * Rest props (including a runtime-injected ref) pass through to the button —
46
+ * required when the tab is a popover trigger, where the positioner's anchor
47
+ * ref arrives via asChild and a closed prop set would silently drop it.
48
+ */
49
+ declare function ModalityTab({ active, onClick, children, size, disabled, className, ...rest }: ModalityTabOwnProps & Omit<ComponentPropsWithoutRef<"button">, keyof ModalityTabOwnProps>): react_jsx_runtime.JSX.Element;
50
+
51
+ type ModelAccessIconProps = {
12
52
  paidOnly?: boolean;
53
+ className?: string;
13
54
  };
55
+ /** Compact model-access marker shared by every model picker. */
56
+ declare function ModelAccessIcon({ paidOnly, className, }: ModelAccessIconProps): react_jsx_runtime.JSX.Element;
57
+
58
+ type ModelSelectorCategory = ModelCategory;
14
59
  type ModelSelectorProps = {
15
- models: readonly ModelSelectorItem[];
60
+ models: readonly ModelInfo[];
16
61
  category: ModelSelectorCategory;
17
62
  value: string;
18
63
  isLoading?: boolean;
19
64
  onChange: (modelId: string) => void;
20
65
  };
21
- declare const CATEGORY_LABELS: Record<ModelSelectorCategory, string>;
22
66
  /** Human-readable label for a model category, e.g. "embedding" -> "Embeddings". */
23
67
  declare function categoryLabel(category: ModelSelectorCategory): string;
24
68
  declare function ModelSelector({ models, category, value, isLoading, onChange, }: ModelSelectorProps): react_jsx_runtime.JSX.Element;
25
69
 
26
- /** Pollinations model modality to package theme mapping. */
70
+ /** Pollinations model-modality colors. Single-accent model: modality is NOT a
71
+ * page theme — each modality is a fixed color from the `--polli-color-modality-*`
72
+ * tokens, applied as a dot/badge via inline style (the key is dynamic, so a CSS
73
+ * var beats a Tailwind class). The token VALUES live in styles/tokens.css. */
27
74
 
28
- /** A model modality is just a model category. Single source of truth: the SDK. */
29
- type Modality = ModelCategory;
30
- /** Theme for a known model category. Total — every category has a theme. */
31
- declare function modalityTheme(category: ModelCategory): ThemeName;
32
- /** Theme for an untrusted category string (e.g. user input); null if unknown. */
33
- declare function getModalityTheme(category: string): ThemeName | null;
75
+ /** The canonical modality keys (match the `--polli-color-modality-*` tokens). */
76
+ type ModalityKey = ModelCategory;
77
+ /** Map an untrusted category string to its modality key; null if unknown. */
78
+ declare function getModalityKey(category: string): ModalityKey | null;
79
+ /** CSS var for a modality's faint chip background. */
80
+ declare const modalityBgVar: (key: ModalityKey) => string;
81
+ /** A modality's text color, mixed toward the strong text so it stays readable on its background. */
82
+ declare const modalityTextColor: (key: ModalityKey) => string;
34
83
 
35
- export { CATEGORY_LABELS, type Modality, ModelSelector, type ModelSelectorCategory, type ModelSelectorItem, type ModelSelectorProps, categoryLabel, getModalityTheme, modalityTheme };
84
+ export { ModalityChip, ModalityDot, ModalityTab, ModelAccessIcon, type ModelAccessIconProps, ModelSelector, type ModelSelectorCategory, categoryLabel, getModalityKey, modalityBgVar, modalityTextColor };