@brainfish-ai/components 0.18.4 → 0.18.6
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.
- package/dist/alert-dialog.d.ts +2 -2
- package/dist/article-suggestions-banner.d.ts +16 -0
- package/dist/button-group.d.ts +3 -2
- package/dist/button.d.ts +1 -1
- package/dist/combobox.d.ts +24 -3
- package/dist/confirm-dialog.d.ts +1 -1
- package/dist/div-button.d.ts +1 -1
- package/dist/esm/chunks/{ChatSearch.BJtS7ZMs.js → ChatSearch.DSB-T76c.js} +2 -2
- package/dist/esm/chunks/{ChatSearch.BJtS7ZMs.js.map → ChatSearch.DSB-T76c.js.map} +1 -1
- package/dist/esm/chunks/{combobox.CkN-wAHB.js → combobox.DNYCWyub.js} +40 -21
- package/dist/esm/chunks/combobox.DNYCWyub.js.map +1 -0
- package/dist/esm/components/article-suggestions-banner.js +55 -0
- package/dist/esm/components/article-suggestions-banner.js.map +1 -0
- package/dist/esm/components/chat-search.js +1 -1
- package/dist/esm/components/combobox.js +1 -1
- package/dist/esm/components/header-nav.js +55 -0
- package/dist/esm/components/header-nav.js.map +1 -0
- package/dist/esm/components/ui/button-group.js +23 -8
- package/dist/esm/components/ui/button-group.js.map +1 -1
- package/dist/esm/components/ui/button.js +2 -8
- package/dist/esm/components/ui/button.js.map +1 -1
- package/dist/esm/components/ui/command.js +1 -1
- package/dist/esm/components/ui/command.js.map +1 -1
- package/dist/esm/components/ui/div-button.js +1 -0
- package/dist/esm/components/ui/div-button.js.map +1 -1
- package/dist/esm/global.css +1 -1
- package/dist/esm/index.js +179 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/header-nav.d.ts +28 -0
- package/dist/index.d.ts +134 -9
- package/dist/stats.html +1 -1
- package/dist/two-level-combobox.d.ts +2 -0
- package/package.json +6 -1
- package/components.json +0 -16
- package/dist/esm/chunks/combobox.CkN-wAHB.js.map +0 -1
- package/markdown-editor.plan.md +0 -2101
- package/markdown-editor.spec.md +0 -915
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/components/input-with-tags/input-with-tags.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Plus } from '@phosphor-icons/react';\n\nimport { cn } from '@/lib/utils';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Button } from '@/components/ui/button';\n\nexport interface Tag {\n label: string;\n value: string;\n}\n\nexport interface InputWithTagsProps {\n title: string;\n placeholder?: string;\n tags: Tag[];\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onTagClick?: (tag: Tag) => void;\n singleValue?: boolean; // If true, clicking a tag replaces the value instead of appending\n className?: string;\n inputClassName?: string;\n tagsClassName?: string;\n disabled?: boolean;\n // Standard textarea props\n name?: string;\n id?: string;\n required?: boolean;\n maxLength?: number;\n minLength?: number;\n rows?: number;\n}\n\nexport function InputWithTags({\n title,\n placeholder,\n tags,\n value: controlledValue,\n defaultValue,\n onChange,\n onTagClick,\n singleValue = false,\n className,\n inputClassName,\n tagsClassName,\n disabled,\n name,\n id,\n required,\n maxLength,\n minLength,\n rows,\n}: InputWithTagsProps) {\n // Helper to find tags present in value (tags are appended as: `${value} ${tag.value}`)\n const findTagsInValue = React.useCallback(\n (val: string): Set<string> => {\n if (!val) return new Set();\n const trimmedVal = val.trim();\n const foundTags = new Set<string>();\n\n for (const tag of tags) {\n const tagValue = tag.value.trim();\n if (singleValue) {\n if (trimmedVal === tagValue) {\n foundTags.add(tag.value);\n }\n } else {\n // Check if tag appears as complete segment (tags are appended as: `${value} ${tag.value}`)\n // Need to check: exact match, at start, in middle, or at end\n if (\n trimmedVal === tagValue ||\n trimmedVal.startsWith(tagValue + ' ') ||\n trimmedVal.includes(' ' + tagValue + ' ') ||\n trimmedVal.endsWith(' ' + tagValue)\n ) {\n foundTags.add(tag.value);\n }\n }\n }\n\n return foundTags;\n },\n [tags, singleValue],\n );\n\n // Initialize clickedTags based on initial value\n const initialValue = controlledValue !== undefined ? controlledValue : defaultValue || '';\n const [internalValue, setInternalValue] = React.useState(defaultValue || '');\n const [clickedTags, setClickedTags] = React.useState<Set<string>>(() => findTagsInValue(initialValue));\n\n // Use controlled value if provided, otherwise use internal state\n const value = controlledValue !== undefined ? controlledValue : internalValue;\n\n // Update clickedTags when value or tags change\n React.useEffect(() => {\n const currentValue = controlledValue !== undefined ? controlledValue : internalValue;\n setClickedTags(findTagsInValue(currentValue));\n }, [controlledValue, internalValue, tags, singleValue, findTagsInValue]);\n\n // Filter out clicked tags\n const availableTags = tags.filter((tag) => !clickedTags.has(tag.value));\n\n const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value;\n if (controlledValue === undefined) {\n setInternalValue(newValue);\n // Sync clickedTags with the new value in uncontrolled mode\n setClickedTags(findTagsInValue(newValue));\n }\n onChange?.(newValue);\n };\n\n const handleTagClick = (tag: Tag) => {\n const newValue = singleValue ? tag.value : value ? `${value} ${tag.value}` : tag.value;\n if (controlledValue === undefined) {\n // In uncontrolled mode, update state and sync clickedTags immediately\n setInternalValue(newValue);\n // Sync clickedTags with the new value\n if (singleValue) {\n setClickedTags(new Set([tag.value]));\n } else {\n setClickedTags(findTagsInValue(newValue));\n }\n }\n // In controlled mode, only call onChange - let useEffect sync clickedTags when controlledValue updates\n onChange?.(newValue);\n onTagClick?.(tag);\n };\n\n return (\n <div className={cn('w-full flex flex-col gap-3', className)}>\n {title && (\n <label htmlFor={id} className=\"text-sm font-semibold\">\n {title}\n </label>\n )}\n <div className=\"relative w-full\">\n <Textarea\n id={id}\n name={name}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n maxLength={maxLength}\n minLength={minLength}\n rows={rows}\n className={inputClassName}\n />\n </div>\n {availableTags.length > 0 && (\n <div className={cn('flex flex-wrap gap-2', tagsClassName)}>\n {availableTags.map((tag, index) => (\n <Button\n key={`${tag.value}-${index}`}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleTagClick(tag)}\n disabled={disabled}\n className=\"rounded-full\"\n >\n <Plus size={16} className=\"text-muted-foreground\" />\n <span>{tag.label}</span>\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCO,SAAS,aAAA,CAAc;AAAA,EAC5B,KAAA;AAAA,EACA,WAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA,EAAO,eAAA;AAAA,EACP,YAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA,GAAc,KAAA;AAAA,EACd,SAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA,IAAA;AAAA,EACA,EAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAuB;AAErB,EAAA,MAAM,kBAAkB,KAAA,CAAM,WAAA;AAAA,IAC5B,CAAC,GAAA,KAA6B;AAC5B,MAAA,IAAI,CAAC,GAAA,EAAK,uBAAO,IAAI,GAAA,EAAI;AACzB,MAAA,MAAM,UAAA,GAAa,IAAI,IAAA,EAAK;AAC5B,MAAA,MAAM,SAAA,uBAAgB,GAAA,EAAY;AAElC,MAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,QAAA,MAAM,QAAA,GAAW,GAAA,CAAI,KAAA,CAAM,IAAA,EAAK;AAChC,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,YAAA,SAAA,CAAU,GAAA,CAAI,IAAI,KAAK,CAAA;AAAA,UACzB;AAAA,QACF,CAAA,MAAO;AAGL,UAAA,IACE,eAAe,QAAA,IACf,UAAA,CAAW,UAAA,CAAW,QAAA,GAAW,GAAG,CAAA,IACpC,UAAA,CAAW,QAAA,CAAS,GAAA,GAAM,WAAW,GAAG,CAAA,IACxC,WAAW,QAAA,CAAS,GAAA,GAAM,QAAQ,CAAA,EAClC;AACA,YAAA,SAAA,CAAU,GAAA,CAAI,IAAI,KAAK,CAAA;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAEA,MAAA,OAAO,SAAA;AAAA,IACT,CAAA;AAAA,IACA,CAAC,MAAM,WAAW;AAAA,GACpB;AAGA,EAAA,MAAM,YAAA,GAAe,eAAA,KAAoB,MAAA,GAAY,eAAA,GAAkB,YAAA,IAAgB,EAAA;AACvF,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,IAAI,KAAA,CAAM,QAAA,CAAS,gBAAgB,EAAE,CAAA;AAC3E,EAAA,MAAM,CAAC,aAAa,cAAc,CAAA,GAAI,MAAM,QAAA,CAAsB,MAAM,eAAA,CAAgB,YAAY,CAAC,CAAA;AAGrG,EAAA,MAAM,KAAA,GAAQ,eAAA,KAAoB,MAAA,GAAY,eAAA,GAAkB,aAAA;AAGhE,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAA,MAAM,YAAA,GAAe,eAAA,KAAoB,MAAA,GAAY,eAAA,GAAkB,aAAA;AACvE,IAAA,cAAA,CAAe,eAAA,CAAgB,YAAY,CAAC,CAAA;AAAA,EAC9C,GAAG,CAAC,eAAA,EAAiB,eAAe,IAAA,EAAM,WAAA,EAAa,eAAe,CAAC,CAAA;AAGvE,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,CAAC,GAAA,KAAQ,CAAC,WAAA,CAAY,GAAA,CAAI,GAAA,CAAI,KAAK,CAAC,CAAA;AAEtE,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAA8C;AACvE,IAAA,MAAM,QAAA,GAAW,EAAE,MAAA,CAAO,KAAA;AAC1B,IAAA,IAAI,oBAAoB,MAAA,EAAW;AACjC,MAAA,gBAAA,CAAiB,QAAQ,CAAA;AAEzB,MAAA,cAAA,CAAe,eAAA,CAAgB,QAAQ,CAAC,CAAA;AAAA,IAC1C;AACA,IAAA,QAAA,GAAW,QAAQ,CAAA;AAAA,EACrB,CAAA;AAEA,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,KAAa;AACnC,IAAA,MAAM,QAAA,GAAW,WAAA,GAAc,GAAA,CAAI,KAAA,GAAQ,KAAA,GAAQ,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,GAAA,CAAI,KAAK,CAAA,CAAA,GAAK,GAAA,CAAI,KAAA;AACjF,IAAA,IAAI,oBAAoB,MAAA,EAAW;AAEjC,MAAA,gBAAA,CAAiB,QAAQ,CAAA;AAEzB,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,cAAA,qBAAmB,GAAA,CAAI,CAAC,GAAA,CAAI,KAAK,CAAC,CAAC,CAAA;AAAA,MACrC,CAAA,MAAO;AACL,QAAA,cAAA,CAAe,eAAA,CAAgB,QAAQ,CAAC,CAAA;AAAA,MAC1C;AAAA,IACF;AAEA,IAAA,QAAA,GAAW,QAAQ,CAAA;AACnB,IAAA,UAAA,GAAa,GAAG,CAAA;AAAA,EAClB,CAAA;AAEA,EAAA,2CACG,KAAA,EAAA,EAAI,SAAA,EAAW,GAAG,4BAAA,EAA8B,SAAS,KACvD,KAAA,oBACC,KAAA,CAAA,aAAA,CAAC,WAAM,OAAA,EAAS,EAAA,EAAI,WAAU,uBAAA,EAAA,EAC3B,KACH,mBAEF,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,WAAU,iBAAA,EAAA,kBACb,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,EAAA;AAAA,MACA,IAAA;AAAA,MACA,WAAA;AAAA,MACA,KAAA;AAAA,MACA,QAAA,EAAU,iBAAA;AAAA,MACV,QAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,IAAA;AAAA,MACA,SAAA,EAAW;AAAA;AAAA,GAEf,CAAA,EACC,aAAA,CAAc,MAAA,GAAS,CAAA,wCACrB,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,sBAAA,EAAwB,aAAa,CAAA,EAAA,EACrD,aAAA,CAAc,GAAA,CAAI,CAAC,KAAK,KAAA,qBACvB,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,CAAA,EAAG,GAAA,CAAI,KAAK,IAAI,KAAK,CAAA,CAAA;AAAA,MAC1B,IAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAQ,SAAA;AAAA,MACR,IAAA,EAAK,IAAA;AAAA,MACL,OAAA,EAAS,MAAM,cAAA,CAAe,GAAG,CAAA;AAAA,MACjC,QAAA;AAAA,MACA,SAAA,EAAU;AAAA,KAAA;AAAA,oBAEV,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAM,EAAA,EAAI,WAAU,uBAAA,EAAwB,CAAA;AAAA,oBAClD,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAA,EAAM,GAAA,CAAI,KAAM;AAAA,GAEpB,CACH,CAEJ,CAAA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/components/input-with-tags/input-with-tags.tsx","../../src/components/font-picker/font-utils.ts","../../src/components/font-picker/use-font-options.ts","../../src/components/font-picker/font-picker-item.tsx","../../src/components/font-picker/font-picker.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Plus } from '@phosphor-icons/react';\n\nimport { cn } from '@/lib/utils';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Button } from '@/components/ui/button';\n\nexport interface Tag {\n label: string;\n value: string;\n}\n\nexport interface InputWithTagsProps {\n title: string;\n placeholder?: string;\n tags: Tag[];\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onTagClick?: (tag: Tag) => void;\n singleValue?: boolean; // If true, clicking a tag replaces the value instead of appending\n className?: string;\n inputClassName?: string;\n tagsClassName?: string;\n disabled?: boolean;\n // Standard textarea props\n name?: string;\n id?: string;\n required?: boolean;\n maxLength?: number;\n minLength?: number;\n rows?: number;\n}\n\nexport function InputWithTags({\n title,\n placeholder,\n tags,\n value: controlledValue,\n defaultValue,\n onChange,\n onTagClick,\n singleValue = false,\n className,\n inputClassName,\n tagsClassName,\n disabled,\n name,\n id,\n required,\n maxLength,\n minLength,\n rows,\n}: InputWithTagsProps) {\n // Helper to find tags present in value (tags are appended as: `${value} ${tag.value}`)\n const findTagsInValue = React.useCallback(\n (val: string): Set<string> => {\n if (!val) return new Set();\n const trimmedVal = val.trim();\n const foundTags = new Set<string>();\n\n for (const tag of tags) {\n const tagValue = tag.value.trim();\n if (singleValue) {\n if (trimmedVal === tagValue) {\n foundTags.add(tag.value);\n }\n } else {\n // Check if tag appears as complete segment (tags are appended as: `${value} ${tag.value}`)\n // Need to check: exact match, at start, in middle, or at end\n if (\n trimmedVal === tagValue ||\n trimmedVal.startsWith(tagValue + ' ') ||\n trimmedVal.includes(' ' + tagValue + ' ') ||\n trimmedVal.endsWith(' ' + tagValue)\n ) {\n foundTags.add(tag.value);\n }\n }\n }\n\n return foundTags;\n },\n [tags, singleValue],\n );\n\n // Initialize clickedTags based on initial value\n const initialValue = controlledValue !== undefined ? controlledValue : defaultValue || '';\n const [internalValue, setInternalValue] = React.useState(defaultValue || '');\n const [clickedTags, setClickedTags] = React.useState<Set<string>>(() => findTagsInValue(initialValue));\n\n // Use controlled value if provided, otherwise use internal state\n const value = controlledValue !== undefined ? controlledValue : internalValue;\n\n // Update clickedTags when value or tags change\n React.useEffect(() => {\n const currentValue = controlledValue !== undefined ? controlledValue : internalValue;\n setClickedTags(findTagsInValue(currentValue));\n }, [controlledValue, internalValue, tags, singleValue, findTagsInValue]);\n\n // Filter out clicked tags\n const availableTags = tags.filter((tag) => !clickedTags.has(tag.value));\n\n const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const newValue = e.target.value;\n if (controlledValue === undefined) {\n setInternalValue(newValue);\n // Sync clickedTags with the new value in uncontrolled mode\n setClickedTags(findTagsInValue(newValue));\n }\n onChange?.(newValue);\n };\n\n const handleTagClick = (tag: Tag) => {\n const newValue = singleValue ? tag.value : value ? `${value} ${tag.value}` : tag.value;\n if (controlledValue === undefined) {\n // In uncontrolled mode, update state and sync clickedTags immediately\n setInternalValue(newValue);\n // Sync clickedTags with the new value\n if (singleValue) {\n setClickedTags(new Set([tag.value]));\n } else {\n setClickedTags(findTagsInValue(newValue));\n }\n }\n // In controlled mode, only call onChange - let useEffect sync clickedTags when controlledValue updates\n onChange?.(newValue);\n onTagClick?.(tag);\n };\n\n return (\n <div className={cn('w-full flex flex-col gap-3', className)}>\n {title && (\n <label htmlFor={id} className=\"text-sm font-semibold\">\n {title}\n </label>\n )}\n <div className=\"relative w-full\">\n <Textarea\n id={id}\n name={name}\n placeholder={placeholder}\n value={value}\n onChange={handleInputChange}\n disabled={disabled}\n required={required}\n maxLength={maxLength}\n minLength={minLength}\n rows={rows}\n className={inputClassName}\n />\n </div>\n {availableTags.length > 0 && (\n <div className={cn('flex flex-wrap gap-2', tagsClassName)}>\n {availableTags.map((tag, index) => (\n <Button\n key={`${tag.value}-${index}`}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleTagClick(tag)}\n disabled={disabled}\n className=\"rounded-full\"\n >\n <Plus size={16} className=\"text-muted-foreground\" />\n <span>{tag.label}</span>\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","import { FontOption } from './types';\n\n// Cache keys for localStorage\nconst FONT_CACHE_KEY = 'brainfish-google-fonts-cache';\nconst FONT_CACHE_TIMESTAMP_KEY = 'brainfish-google-fonts-cache-timestamp';\nconst FONT_CACHE_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n// Cache for loaded font stylesheets to prevent duplicate loads\nconst loadedFonts = new Set<string>();\n\n/**\n * Get cached fonts from localStorage\n */\nexport const getCachedFonts = (): FontOption[] | null => {\n try {\n const timestamp = localStorage.getItem(FONT_CACHE_TIMESTAMP_KEY);\n if (!timestamp) return null;\n\n const cacheAge = Date.now() - parseInt(timestamp, 10);\n if (cacheAge > FONT_CACHE_DURATION_MS) {\n localStorage.removeItem(FONT_CACHE_KEY);\n localStorage.removeItem(FONT_CACHE_TIMESTAMP_KEY);\n\n return null;\n }\n\n const cached = localStorage.getItem(FONT_CACHE_KEY);\n if (!cached) return null;\n\n return JSON.parse(cached) as FontOption[];\n } catch {\n return null;\n }\n};\n\n/**\n * Save fonts to localStorage cache\n */\nexport const setCachedFonts = (fonts: FontOption[]): void => {\n try {\n localStorage.setItem(FONT_CACHE_KEY, JSON.stringify(fonts));\n localStorage.setItem(FONT_CACHE_TIMESTAMP_KEY, Date.now().toString());\n } catch {\n // localStorage might be full or unavailable, ignore\n }\n};\n\n/**\n * Load a Google Font stylesheet dynamically\n */\nexport const loadFont = (fontFamily: string): void => {\n if (loadedFonts.has(fontFamily)) return;\n\n const link = document.createElement('link');\n link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}&display=swap`;\n link.rel = 'stylesheet';\n document.head.appendChild(link);\n loadedFonts.add(fontFamily);\n};\n\n/**\n * Clear the font cache from localStorage\n * Useful for forcing a fresh fetch of fonts\n */\nexport const clearFontCache = (): void => {\n try {\n localStorage.removeItem(FONT_CACHE_KEY);\n localStorage.removeItem(FONT_CACHE_TIMESTAMP_KEY);\n } catch {\n // localStorage might be unavailable, ignore\n }\n};\n","import * as React from 'react';\n\nimport { FontOption } from './types';\nimport { getCachedFonts, setCachedFonts, loadFont } from './font-utils';\n\nexport interface UseFontOptionsProps {\n apiKey: string;\n maxFonts?: number;\n onError?: (error: Error) => void;\n}\n\nexport interface UseFontOptionsReturn {\n fonts: FontOption[];\n isLoading: boolean;\n loadFont: typeof loadFont;\n}\n\n/**\n * Hook to fetch and manage Google Fonts options\n * Handles caching, loading state, and font stylesheet loading\n */\nexport function useFontOptions({ apiKey, maxFonts = 500, onError }: UseFontOptionsProps): UseFontOptionsReturn {\n const [fonts, setFonts] = React.useState<FontOption[]>([]);\n const [isLoading, setIsLoading] = React.useState(true);\n\n // Stabilize onError callback reference to prevent effect re-runs when\n // consumers pass inline functions (which create new references on every render)\n const onErrorRef = React.useRef(onError);\n React.useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n React.useEffect(() => {\n const fetchFonts = async () => {\n // Check cache first\n const cachedFonts = getCachedFonts();\n if (cachedFonts && cachedFonts.length > 0) {\n setFonts(cachedFonts.slice(0, maxFonts));\n setIsLoading(false);\n\n return;\n }\n\n try {\n const response = await fetch(`https://www.googleapis.com/webfonts/v1/webfonts?key=${apiKey}&sort=popularity`);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch fonts: ${response.statusText}`);\n }\n\n const data = await response.json();\n\n const fontOptions: FontOption[] = data.items.map((fontItem: { family: string; category: string; variants: string[] }) => ({\n value: fontItem.family,\n label: fontItem.family,\n category: fontItem.category,\n variants: fontItem.variants,\n }));\n\n // Cache all fonts, but only display up to maxFonts\n setCachedFonts(fontOptions);\n setFonts(fontOptions.slice(0, maxFonts));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch fonts');\n // eslint-disable-next-line no-console\n console.error('FontPicker: Failed to fetch Google Fonts', error);\n onErrorRef.current?.(error);\n } finally {\n setIsLoading(false);\n }\n };\n\n void fetchFonts();\n }, [apiKey, maxFonts]);\n\n return { fonts, isLoading, loadFont };\n}\n","import * as React from 'react';\nimport { Check } from '@phosphor-icons/react';\n\nimport { FontOption } from './types';\nimport { loadFont } from './font-utils';\n\nimport { cn } from '@/lib/utils';\nimport { CommandItem } from '@/components/ui/command';\n\nexport interface FontPickerItemProps {\n font: FontOption;\n isSelected: boolean;\n onSelect: () => void;\n}\n\n/**\n * Individual font item in the dropdown list\n * Loads font on mount for preview using Intersection Observer\n * Memoized to prevent unnecessary re-renders\n */\nexport const FontPickerItem = React.memo(function FontPickerItem({ font, isSelected, onSelect }: FontPickerItemProps) {\n const itemRef = React.useRef<HTMLDivElement>(null);\n const [isVisible, setIsVisible] = React.useState(false);\n\n // Use Intersection Observer to lazy-load fonts\n React.useEffect(() => {\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setIsVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.1 },\n );\n\n if (itemRef.current) {\n observer.observe(itemRef.current);\n }\n\n return () => observer.disconnect();\n }, []);\n\n // Load font when visible\n React.useEffect(() => {\n if (isVisible) {\n loadFont(font.value);\n }\n }, [isVisible, font.value]);\n\n // Build accessible label\n const ariaLabel = `Select ${font.label} font${font.category ? `, ${font.category} category` : ''}`;\n\n return (\n <CommandItem\n ref={itemRef}\n value={font.label}\n onSelect={onSelect}\n className=\"cursor-pointer\"\n aria-label={ariaLabel}\n aria-selected={isSelected}\n >\n <span style={{ fontFamily: isVisible ? font.value : undefined }} className=\"flex-1 truncate\">\n {font.label}\n </span>\n {font.category && <span className=\"text-xs text-muted-foreground capitalize\">{font.category}</span>}\n <Check className={cn('ml-2 h-4 w-4', isSelected ? 'opacity-100' : 'opacity-0')} />\n </CommandItem>\n );\n});\n","import * as React from 'react';\nimport { SpinnerGap } from '@phosphor-icons/react';\n\nimport { useFontOptions } from './use-font-options';\nimport { FontPickerItem } from './font-picker-item';\n\nimport { Combobox } from '@/components/combobox/combobox';\n\nexport interface FontPickerProps {\n /** Currently selected font family */\n value?: string;\n /** Callback when font selection changes */\n onChange?: (fontFamily: string) => void;\n /** Google Fonts API key (required) */\n apiKey: string;\n /** Placeholder text when no font is selected */\n placeholder?: string;\n /** Text shown in search input */\n searchPlaceholder?: string;\n /** Text shown when no fonts match the search */\n noResultsText?: string;\n /** Text shown while fonts are loading */\n loadingText?: string;\n /** Whether the picker is disabled */\n disabled?: boolean;\n /** Additional CSS classes for the trigger button */\n className?: string;\n /** Width of the dropdown (defaults to trigger width) */\n dropdownWidth?: number;\n /** Maximum number of fonts to display (for performance) */\n maxFonts?: number;\n /** Callback when an error occurs fetching fonts */\n onError?: (error: Error) => void;\n}\n\n/**\n * FontPicker - A searchable dropdown for selecting Google Fonts\n *\n * @example\n * ```tsx\n * const [font, setFont] = useState('Inter');\n *\n * <FontPicker\n * apiKey=\"YOUR_API_KEY\"\n * value={font}\n * onChange={setFont}\n * placeholder=\"Select a font\"\n * />\n * ```\n */\nexport function FontPicker({\n value,\n onChange,\n apiKey,\n placeholder = 'Select a font...',\n searchPlaceholder = 'Search fonts...',\n noResultsText = 'No fonts found.',\n loadingText = 'Loading fonts...',\n disabled = false,\n className,\n dropdownWidth,\n maxFonts = 500,\n onError,\n}: FontPickerProps) {\n const { fonts, isLoading, loadFont } = useFontOptions({ apiKey, maxFonts, onError });\n\n // Load font stylesheet when selected\n React.useEffect(() => {\n if (value) {\n loadFont(value);\n }\n }, [value, loadFont]);\n\n // fonts already have value/label which Combobox requires\n const options = fonts;\n\n return (\n <Combobox\n options={options}\n value={value}\n onChange={onChange}\n defaultValueLabel={placeholder}\n placeholder={searchPlaceholder}\n noResultsLabel={noResultsText}\n disabled={disabled || isLoading}\n className={className}\n dropdownWidth={dropdownWidth}\n renderTrigger={({ selectedItem }) => {\n if (isLoading) {\n return (\n <span className=\"flex items-center gap-2 text-muted-foreground\">\n <SpinnerGap className=\"h-4 w-4 animate-spin\" />\n {loadingText}\n </span>\n );\n }\n\n if (selectedItem) {\n return <span style={{ fontFamily: selectedItem.value }}>{selectedItem.label}</span>;\n }\n\n return <span className=\"text-muted-foreground\">{placeholder}</span>;\n }}\n renderItem={({ item, isSelected, onSelect }) => (\n <FontPickerItem font={item} isSelected={isSelected} onSelect={onSelect} />\n )}\n />\n );\n}\n\nexport default FontPicker;\n"],"names":["FontPickerItem"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
2
|
+
import * as React_2 from 'react';
|
|
3
|
+
|
|
4
|
+
export declare type AccountDropdownItem = {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
declare interface AccountDropdownProps extends React_2.ComponentPropsWithoutRef<typeof DropdownMenu> {
|
|
11
|
+
accountPhoto: string;
|
|
12
|
+
accountName: string;
|
|
13
|
+
accountDropdownItems: AccountDropdownItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare const DropdownMenu: React_2.FC<DropdownMenuPrimitive.DropdownMenuProps>;
|
|
17
|
+
|
|
18
|
+
export declare const HeaderNav: React_2.ForwardRefExoticComponent<HeaderNavProps & React_2.RefAttributes<HTMLElement>>;
|
|
19
|
+
|
|
20
|
+
declare interface HeaderNavProps extends React_2.ComponentPropsWithoutRef<'header'>, Omit<AccountDropdownProps, 'dir'> {
|
|
21
|
+
onSideNavigationToggle: () => void;
|
|
22
|
+
onHelpClick: () => void;
|
|
23
|
+
showSuggestionAction?: boolean;
|
|
24
|
+
suggestionAction?: React_2.ReactNode;
|
|
25
|
+
onSuggestionActionClick?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { }
|
package/dist/index.d.ts
CHANGED
|
@@ -149,14 +149,14 @@ export declare const AlertDescription: React_2.ForwardRefExoticComponent<Omit<Re
|
|
|
149
149
|
export declare const AlertDialog: React_2.ForwardRefExoticComponent<AlertDialogPrimitive.AlertDialogProps & React_2.RefAttributes<never>>;
|
|
150
150
|
|
|
151
151
|
export declare const AlertDialogAction: React_2.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogActionProps & React_2.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
152
|
-
variant?: "link" | "dark" | "default" | "destructive" | "success" | "outline" | "secondary" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
152
|
+
variant?: "link" | "dark" | "default" | "destructive" | "success" | "outline" | "secondary" | "suggestion" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
153
153
|
size?: "default" | "icon" | "xs" | "sm" | "lg" | null | undefined;
|
|
154
154
|
} & ClassProp) | undefined) => string> & {
|
|
155
155
|
asChild?: boolean | undefined;
|
|
156
156
|
} & React_2.RefAttributes<HTMLButtonElement>>;
|
|
157
157
|
|
|
158
158
|
export declare const AlertDialogCancel: React_2.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogCancelProps & React_2.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
159
|
-
variant?: "link" | "dark" | "default" | "destructive" | "success" | "outline" | "secondary" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
159
|
+
variant?: "link" | "dark" | "default" | "destructive" | "success" | "outline" | "secondary" | "suggestion" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
160
160
|
size?: "default" | "icon" | "xs" | "sm" | "lg" | null | undefined;
|
|
161
161
|
} & ClassProp) | undefined) => string> & React_2.RefAttributes<HTMLButtonElement>>;
|
|
162
162
|
|
|
@@ -424,16 +424,17 @@ export declare const BrainfishColors: {
|
|
|
424
424
|
|
|
425
425
|
export declare const Button: React_2.ForwardRefExoticComponent<ButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
|
|
426
426
|
|
|
427
|
-
export declare function ButtonGroup({ className, orientation, ...props }: default_2.ComponentProps<
|
|
427
|
+
export declare function ButtonGroup({ className, orientation, rounded, ...props }: default_2.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>): default_2.JSX.Element;
|
|
428
428
|
|
|
429
429
|
export declare function ButtonGroupSeparator({ className, orientation, ...props }: default_2.ComponentProps<typeof Separator>): default_2.JSX.Element;
|
|
430
430
|
|
|
431
|
-
export declare function ButtonGroupText({ className, asChild, ...props }: default_2.ComponentProps<
|
|
431
|
+
export declare function ButtonGroupText({ className, asChild, ...props }: default_2.ComponentProps<'div'> & {
|
|
432
432
|
asChild?: boolean;
|
|
433
433
|
}): default_2.JSX.Element;
|
|
434
434
|
|
|
435
435
|
export declare const buttonGroupVariants: (props?: ({
|
|
436
436
|
orientation?: "horizontal" | "vertical" | null | undefined;
|
|
437
|
+
rounded?: boolean | null | undefined;
|
|
437
438
|
} & ClassProp) | undefined) => string;
|
|
438
439
|
|
|
439
440
|
export declare interface ButtonProps extends React_2.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -441,7 +442,7 @@ export declare interface ButtonProps extends React_2.ButtonHTMLAttributes<HTMLBu
|
|
|
441
442
|
}
|
|
442
443
|
|
|
443
444
|
export declare const buttonVariants: (props?: ({
|
|
444
|
-
variant?: "link" | "dark" | "default" | "destructive" | "success" | "outline" | "secondary" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
445
|
+
variant?: "link" | "dark" | "default" | "destructive" | "success" | "outline" | "secondary" | "suggestion" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
445
446
|
size?: "default" | "icon" | "xs" | "sm" | "lg" | null | undefined;
|
|
446
447
|
} & ClassProp) | undefined) => string;
|
|
447
448
|
|
|
@@ -572,6 +573,12 @@ declare type ClearAll = {
|
|
|
572
573
|
type: typeof AnswersActions.CLEAR_ALL;
|
|
573
574
|
};
|
|
574
575
|
|
|
576
|
+
/**
|
|
577
|
+
* Clear the font cache from localStorage
|
|
578
|
+
* Useful for forcing a fresh fetch of fonts
|
|
579
|
+
*/
|
|
580
|
+
export declare const clearFontCache: () => void;
|
|
581
|
+
|
|
575
582
|
declare type ClearUserFeedback = {
|
|
576
583
|
type: typeof AnswersActions.CLEAR_USER_FEEDBACK;
|
|
577
584
|
payload: {
|
|
@@ -593,24 +600,45 @@ export declare interface Collection {
|
|
|
593
600
|
siteEnabled?: boolean;
|
|
594
601
|
}
|
|
595
602
|
|
|
596
|
-
export declare function Combobox({ options, defaultValueLabel, placeholder, noResultsLabel, className, onOpenChange, onChange, dropdownWidth, value: controlledValue, }: ComboboxProps): React_2.JSX.Element;
|
|
603
|
+
export declare function Combobox<T extends ComboboxItem = ComboboxItem>({ options, defaultValueLabel, placeholder, noResultsLabel, className, onOpenChange, onChange, dropdownWidth, value: controlledValue, disabled, renderItem, renderValue, renderTrigger, }: ComboboxProps<T>): React_2.JSX.Element;
|
|
597
604
|
|
|
598
605
|
export declare interface ComboboxItem {
|
|
599
606
|
value: string;
|
|
600
607
|
label: string;
|
|
601
608
|
icon?: React_2.ReactNode;
|
|
609
|
+
/** Additional data that can be passed to custom renderers */
|
|
610
|
+
data?: any;
|
|
602
611
|
}
|
|
603
612
|
|
|
604
|
-
export declare interface ComboboxProps {
|
|
605
|
-
options:
|
|
613
|
+
export declare interface ComboboxProps<T extends ComboboxItem = ComboboxItem> {
|
|
614
|
+
options: T[];
|
|
606
615
|
defaultValueLabel: string;
|
|
607
616
|
placeholder: string;
|
|
608
617
|
noResultsLabel: string;
|
|
609
618
|
className?: string;
|
|
610
619
|
dropdownWidth?: number;
|
|
611
620
|
value?: string;
|
|
621
|
+
/** Whether the combobox is disabled */
|
|
622
|
+
disabled?: boolean;
|
|
623
|
+
/** Callback on open state change */
|
|
612
624
|
onOpenChange?: (open: boolean) => void;
|
|
625
|
+
/** Callback on value change */
|
|
613
626
|
onChange?: (value: string) => void;
|
|
627
|
+
/** Custom render function for each item in the dropdown */
|
|
628
|
+
renderItem?: (props: ComboboxRenderItemProps<T>) => React_2.ReactNode;
|
|
629
|
+
/** Custom render function for the selected value in the trigger button */
|
|
630
|
+
renderValue?: (item: T) => React_2.ReactNode;
|
|
631
|
+
/** Custom render function for the entire trigger button content */
|
|
632
|
+
renderTrigger?: (props: {
|
|
633
|
+
selectedItem: T | undefined;
|
|
634
|
+
isOpen: boolean;
|
|
635
|
+
}) => React_2.ReactNode;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export declare interface ComboboxRenderItemProps<T extends ComboboxItem = ComboboxItem> {
|
|
639
|
+
item: T;
|
|
640
|
+
isSelected: boolean;
|
|
641
|
+
onSelect: () => void;
|
|
614
642
|
}
|
|
615
643
|
|
|
616
644
|
export declare const Command: React_2.ForwardRefExoticComponent<Omit<{
|
|
@@ -762,7 +790,7 @@ export declare interface DivButtonProps extends React_2.HTMLAttributes<HTMLDivEl
|
|
|
762
790
|
}
|
|
763
791
|
|
|
764
792
|
export declare const divButtonVariants: (props?: ({
|
|
765
|
-
variant?: "link" | "dark" | "default" | "destructive" | "outline" | "secondary" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
793
|
+
variant?: "link" | "dark" | "default" | "destructive" | "outline" | "secondary" | "suggestion" | "ghost" | "shadow" | "shadowSurface" | null | undefined;
|
|
766
794
|
size?: "default" | "icon" | "sm" | "lg" | null | undefined;
|
|
767
795
|
} & ClassProp) | undefined) => string;
|
|
768
796
|
|
|
@@ -908,6 +936,70 @@ export declare interface FilterValue {
|
|
|
908
936
|
value?: string | string[];
|
|
909
937
|
}
|
|
910
938
|
|
|
939
|
+
export declare interface FontOption {
|
|
940
|
+
value: string;
|
|
941
|
+
label: string;
|
|
942
|
+
category?: string;
|
|
943
|
+
variants?: string[];
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* FontPicker - A searchable dropdown for selecting Google Fonts
|
|
948
|
+
*
|
|
949
|
+
* @example
|
|
950
|
+
* ```tsx
|
|
951
|
+
* const [font, setFont] = useState('Inter');
|
|
952
|
+
*
|
|
953
|
+
* <FontPicker
|
|
954
|
+
* apiKey="YOUR_API_KEY"
|
|
955
|
+
* value={font}
|
|
956
|
+
* onChange={setFont}
|
|
957
|
+
* placeholder="Select a font"
|
|
958
|
+
* />
|
|
959
|
+
* ```
|
|
960
|
+
*/
|
|
961
|
+
export declare function FontPicker({ value, onChange, apiKey, placeholder, searchPlaceholder, noResultsText, loadingText, disabled, className, dropdownWidth, maxFonts, onError, }: FontPickerProps): React_2.JSX.Element;
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Individual font item in the dropdown list
|
|
965
|
+
* Loads font on mount for preview using Intersection Observer
|
|
966
|
+
* Memoized to prevent unnecessary re-renders
|
|
967
|
+
*/
|
|
968
|
+
export declare const FontPickerItem: React_2.NamedExoticComponent<FontPickerItemProps>;
|
|
969
|
+
|
|
970
|
+
export declare interface FontPickerItemProps {
|
|
971
|
+
font: FontOption;
|
|
972
|
+
isSelected: boolean;
|
|
973
|
+
onSelect: () => void;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
export declare interface FontPickerProps {
|
|
977
|
+
/** Currently selected font family */
|
|
978
|
+
value?: string;
|
|
979
|
+
/** Callback when font selection changes */
|
|
980
|
+
onChange?: (fontFamily: string) => void;
|
|
981
|
+
/** Google Fonts API key (required) */
|
|
982
|
+
apiKey: string;
|
|
983
|
+
/** Placeholder text when no font is selected */
|
|
984
|
+
placeholder?: string;
|
|
985
|
+
/** Text shown in search input */
|
|
986
|
+
searchPlaceholder?: string;
|
|
987
|
+
/** Text shown when no fonts match the search */
|
|
988
|
+
noResultsText?: string;
|
|
989
|
+
/** Text shown while fonts are loading */
|
|
990
|
+
loadingText?: string;
|
|
991
|
+
/** Whether the picker is disabled */
|
|
992
|
+
disabled?: boolean;
|
|
993
|
+
/** Additional CSS classes for the trigger button */
|
|
994
|
+
className?: string;
|
|
995
|
+
/** Width of the dropdown (defaults to trigger width) */
|
|
996
|
+
dropdownWidth?: number;
|
|
997
|
+
/** Maximum number of fonts to display (for performance) */
|
|
998
|
+
maxFonts?: number;
|
|
999
|
+
/** Callback when an error occurs fetching fonts */
|
|
1000
|
+
onError?: (error: Error) => void;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
911
1003
|
export declare const formatFileSize: (bytes: number) => string;
|
|
912
1004
|
|
|
913
1005
|
export declare const FormattedMessage: FC<Props>;
|
|
@@ -925,6 +1017,11 @@ declare type GetActionInputs = {
|
|
|
925
1017
|
};
|
|
926
1018
|
};
|
|
927
1019
|
|
|
1020
|
+
/**
|
|
1021
|
+
* Get cached fonts from localStorage
|
|
1022
|
+
*/
|
|
1023
|
+
export declare const getCachedFonts: () => FontOption[] | null;
|
|
1024
|
+
|
|
928
1025
|
/**
|
|
929
1026
|
* A flexible icon component that supports both Phosphor icons and image-based icons.
|
|
930
1027
|
*
|
|
@@ -1013,6 +1110,11 @@ declare type JSONSchema<T> = JSONSchemaType<T>;
|
|
|
1013
1110
|
|
|
1014
1111
|
export declare const Label: React_2.ForwardRefExoticComponent<Omit<LabelPrimitive.LabelProps & React_2.RefAttributes<HTMLLabelElement>, "ref"> & VariantProps<(props?: ClassProp | undefined) => string> & React_2.RefAttributes<HTMLLabelElement>>;
|
|
1015
1112
|
|
|
1113
|
+
/**
|
|
1114
|
+
* Load a Google Font stylesheet dynamically
|
|
1115
|
+
*/
|
|
1116
|
+
export declare const loadFont: (fontFamily: string) => void;
|
|
1117
|
+
|
|
1016
1118
|
export declare const Logo: React_2.ForwardRefExoticComponent<Omit<LogoProps, "ref"> & React_2.RefAttributes<SVGSVGElement>>;
|
|
1017
1119
|
|
|
1018
1120
|
export declare type LogoColor = 'default' | 'outline' | 'fill';
|
|
@@ -1181,6 +1283,11 @@ declare type SetAnswers = {
|
|
|
1181
1283
|
};
|
|
1182
1284
|
};
|
|
1183
1285
|
|
|
1286
|
+
/**
|
|
1287
|
+
* Save fonts to localStorage cache
|
|
1288
|
+
*/
|
|
1289
|
+
export declare const setCachedFonts: (fonts: FontOption[]) => void;
|
|
1290
|
+
|
|
1184
1291
|
declare type SetFollowUpQuestions = {
|
|
1185
1292
|
type: typeof AnswersActions.SET_FOLLOW_UP_QUESTIONS;
|
|
1186
1293
|
payload: {
|
|
@@ -1366,6 +1473,24 @@ declare type UrlString = string & {
|
|
|
1366
1473
|
|
|
1367
1474
|
export declare const useChatSearch: () => ChatSearchContextProps;
|
|
1368
1475
|
|
|
1476
|
+
/**
|
|
1477
|
+
* Hook to fetch and manage Google Fonts options
|
|
1478
|
+
* Handles caching, loading state, and font stylesheet loading
|
|
1479
|
+
*/
|
|
1480
|
+
export declare function useFontOptions({ apiKey, maxFonts, onError }: UseFontOptionsProps): UseFontOptionsReturn;
|
|
1481
|
+
|
|
1482
|
+
export declare interface UseFontOptionsProps {
|
|
1483
|
+
apiKey: string;
|
|
1484
|
+
maxFonts?: number;
|
|
1485
|
+
onError?: (error: Error) => void;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
export declare interface UseFontOptionsReturn {
|
|
1489
|
+
fonts: FontOption[];
|
|
1490
|
+
isLoading: boolean;
|
|
1491
|
+
loadFont: typeof loadFont;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1369
1494
|
export declare const useIsChatSearchDirty: () => boolean;
|
|
1370
1495
|
|
|
1371
1496
|
export declare const ZoomableImage: React_2.FC<ZoomableImageProps>;
|