sanity-plugin-link-field
Version:
A custom Link field for Sanity Studio
1 lines • 28.6 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/helpers/requiredLinkField.ts","../src/components/CustomLinkInput.tsx","../src/components/LinkInput.tsx","../src/components/LinkTypeInput.tsx","../src/linkField.tsx"],"sourcesContent":["import type {CustomValidatorResult} from 'sanity'\n\nimport type {LinkValue} from '../types'\nimport {isCustomLink, isEmailLink, isExternalLink, isInternalLink, isPhoneLink} from './typeGuards'\n\n/**\n * Helper to create a required link field.\n */\nexport const requiredLinkField = (field: unknown): CustomValidatorResult => {\n const link = field as LinkValue\n\n if (!link || !link.type) {\n return 'Link is required'\n }\n\n if (isInternalLink(link) && !link.internalLink) {\n return {\n message: 'Link is required',\n path: 'internalLink',\n }\n }\n\n if (isExternalLink(link) && !link.url) {\n return {\n message: 'URL is required',\n path: 'url',\n }\n }\n\n if (isEmailLink(link) && !link.email) {\n return {\n message: 'E-mail is required',\n path: 'email',\n }\n }\n\n if (isPhoneLink(link) && !link.phone) {\n return {\n message: 'Phone is required',\n path: 'phone',\n }\n }\n\n if (isCustomLink(link) && !link.value) {\n return {\n message: 'Value is required',\n path: 'value',\n }\n }\n\n return true\n}\n","import {Select, Spinner} from '@sanity/ui'\nimport {memo, useEffect, useState} from 'react'\nimport {SanityDocument, set, type StringInputProps, useFormValue, useWorkspace} from 'sanity'\n\nimport {CustomLinkType, CustomLinkTypeOptions, LinkValue} from '../types'\n\n/**\n * Custom input component used for custom link types.\n * Renders a dropdown with the available options for the custom link type.\n */\nexport const CustomLinkInput = memo(function CustomLinkInput(\n props: StringInputProps & {\n customLinkTypes: CustomLinkType[]\n },\n) {\n const workspace = useWorkspace()\n const document = useFormValue([]) as SanityDocument\n const linkValue = useFormValue(props.path.slice(0, -1)) as LinkValue | null\n const [options, setOptions] = useState<CustomLinkTypeOptions[] | null>(null)\n\n const customLinkType = props.customLinkTypes.find((type) => type.value === linkValue!.type)\n\n useEffect(() => {\n if (customLinkType) {\n if (Array.isArray(customLinkType?.options)) {\n setOptions(customLinkType.options)\n } else {\n customLinkType\n .options(document, props.path, workspace.currentUser)\n .then((options) => setOptions(options))\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [customLinkType, props.path, workspace.currentUser])\n\n return options ? (\n <Select\n onChange={(e) => {\n props.onChange(set(e.currentTarget.value || ''))\n }}\n >\n <>\n <option value=\"\" selected={props.value === ''} disabled hidden />\n {options.map((option) => (\n <option key={option.value} value={option.value} selected={props.value === option.value}>\n {option.title}\n </option>\n ))}\n </>\n </Select>\n ) : (\n <Spinner style={{marginLeft: '0.5rem'}} />\n )\n})\n","import {Box, Flex, Stack, Text} from '@sanity/ui'\nimport {memo} from 'react'\nimport {type FieldMember, FormFieldValidationStatus, ObjectInputMember} from 'sanity'\n\nimport {isCustomLink} from '../helpers/typeGuards'\nimport {LinkInputProps} from '../types'\n\n/**\n * Custom input component for the link object.\n * Nicely renders the type and link fields next to each other, with the\n * description and any validation errors for the link field below them.\n *\n * The rest of the fields (\"blank\" and \"advanced\") are rendered as usual.\n */\nexport const LinkInput = memo(function LinkInput(props: LinkInputProps) {\n const [textField, typeField, linkField, ...otherFields] = props.members as FieldMember[]\n const {options} = props.schemaType\n\n const {\n field: {\n validation: linkFieldValidation,\n schemaType: {description: linkFieldDescription},\n },\n } = linkField\n\n const description =\n // If a custom link type is used, use its description if it has one.\n props.value && isCustomLink(props.value)\n ? props.customLinkTypes.find((type) => type.value === props.value?.type)?.description\n : // Fallback to the description of the current link type field.\n linkFieldDescription\n\n const renderProps = {\n renderAnnotation: props.renderAnnotation,\n renderBlock: props.renderBlock,\n renderField: props.renderField,\n renderInlineBlock: props.renderInlineBlock,\n renderInput: props.renderInput,\n renderItem: props.renderItem,\n renderPreview: props.renderPreview,\n }\n\n return (\n <Stack space={4}>\n {/* Render the text field if enabled */}\n {options?.enableText && (\n <ObjectInputMember\n member={{\n ...textField,\n field: {\n ...textField.field,\n schemaType: {\n ...textField.field.schemaType,\n title: options?.textLabel || textField.field.schemaType.title,\n },\n },\n }}\n {...renderProps}\n />\n )}\n\n <Stack space={3}>\n {/* Render a label for the link field if there's also a text field enabled. */}\n {/* If there's no text field, the label here is irrelevant */}\n {options?.enableText && (\n <Text as=\"label\" weight=\"medium\" size={1}>\n Link\n </Text>\n )}\n\n <Flex gap={2} align=\"flex-start\">\n {/* Render the type field (without its label) */}\n <ObjectInputMember\n member={{\n ...typeField,\n field: {\n ...typeField.field,\n schemaType: {\n ...typeField.field.schemaType,\n title: undefined,\n },\n },\n }}\n {...renderProps}\n />\n\n <Stack space={2} style={{width: '100%'}}>\n {/* Render the input for the selected type of link (without its label) */}\n <ObjectInputMember\n member={{\n ...linkField,\n field: {\n ...linkField.field,\n schemaType: {\n ...linkField.field.schemaType,\n title: undefined,\n },\n },\n }}\n {...renderProps}\n />\n\n {/* Render any validation errors for the link field */}\n {linkFieldValidation.length > 0 && (\n <Box\n style={{\n contain: 'size',\n marginBottom: '6px',\n marginLeft: 'auto',\n marginRight: '12px',\n }}\n >\n <FormFieldValidationStatus\n fontSize={1}\n placement=\"top\"\n validation={linkFieldValidation}\n />\n </Box>\n )}\n </Stack>\n </Flex>\n\n {/* Render the description of the selected link field, if any */}\n {description && (\n <Text muted size={1}>\n {description}\n </Text>\n )}\n </Stack>\n\n {/* Render the rest of the fields as usual */}\n {otherFields.map((field) => (\n <ObjectInputMember key={field.key} member={field} {...renderProps} />\n ))}\n </Stack>\n )\n})\n","import {ChevronDownIcon} from '@sanity/icons'\nimport {Button, Menu, MenuButton, MenuItem} from '@sanity/ui'\nimport {AtSignIcon, GlobeIcon, LinkIcon, PhoneIcon, type LucideIcon} from 'lucide-react'\nimport {type ComponentType, memo} from 'react'\nimport {set, type StringInputProps} from 'sanity'\n\nimport {CustomLinkType, LinkFieldPluginOptions, LinkType} from '../types'\n\nconst ICON_SIZE = 16\n\nconst defaultLinkTypes: LinkType[] = [\n {title: 'Internal', value: 'internal', icon: LinkIcon},\n {title: 'URL', value: 'external', icon: GlobeIcon},\n {title: 'Email', value: 'email', icon: AtSignIcon},\n {title: 'Phone', value: 'phone', icon: PhoneIcon},\n]\n\n/**\n * Check if the icon is a lucide icon (one of the default link types)\n */\nfunction isLucideIcon(icon: ComponentType): boolean {\n return defaultLinkTypes.some((t) => t.icon === icon)\n}\n\n/**\n * Create a sized wrapper for lucide icons\n */\nfunction createSizedIcon(Icon: LucideIcon): ComponentType {\n function SizedIcon(props: Record<string, unknown>) {\n return <Icon size={ICON_SIZE} {...props} />\n }\n SizedIcon.displayName = `SizedIcon(${Icon.displayName || Icon.name || 'Unknown'})`\n return SizedIcon\n}\n\n/**\n * Get the icon component for a link type, wrapping lucide icons to set the correct size\n */\nfunction getIcon(type: LinkType): ComponentType {\n if (isLucideIcon(type.icon)) {\n return createSizedIcon(type.icon as LucideIcon)\n }\n return type.icon\n}\n\n/**\n * Custom input component for the \"type\" field on the link object.\n * Renders a button with an icon and a dropdown menu to select the link type.\n */\nexport const LinkTypeInput = memo(function LinkTypeInput({\n value,\n onChange,\n customLinkTypes = [],\n linkableSchemaTypes,\n}: StringInputProps & {\n customLinkTypes?: CustomLinkType[]\n linkableSchemaTypes: LinkFieldPluginOptions['linkableSchemaTypes']\n}) {\n const linkTypes = [\n // Disable internal links if not enabled for any schema types\n ...defaultLinkTypes.filter(\n ({value}) => value !== 'internal' || linkableSchemaTypes?.length > 0,\n ),\n ...customLinkTypes,\n ]\n\n const selectedType = linkTypes.find((type) => type.value === value) || linkTypes[0]\n\n return (\n <MenuButton\n button={\n <Button\n type=\"button\"\n mode=\"ghost\"\n icon={getIcon(selectedType)}\n iconRight={ChevronDownIcon}\n title=\"Select link type\"\n aria-label={`Select link type (currently: ${selectedType.title})`}\n style={{height: '35px'}}\n />\n }\n id=\"link-type\"\n menu={\n <Menu>\n {linkTypes.map((type) => (\n <MenuItem\n key={type.value}\n text={type.title}\n icon={getIcon(type)}\n onClick={() => {\n onChange(set(type.value))\n }}\n />\n ))}\n </Menu>\n }\n />\n )\n})\n","import {defineField, definePlugin, defineType, type ObjectInputProps} from 'sanity'\n\nimport {CustomLinkInput} from './components/CustomLinkInput'\nimport {LinkInput} from './components/LinkInput'\nimport {LinkTypeInput} from './components/LinkTypeInput'\nimport {isCustomLink} from './helpers/typeGuards'\nimport type {LinkFieldPluginOptions, LinkSchemaType, LinkValue} from './types'\n\n/**\n * A plugin that adds a custom Link field for creating internal and external links,\n * as well as `mailto` and `tel`-links, all using the same intuitive UI.\n *\n * @param options - Options for the plugin. See {@link LinkFieldPluginOptions}\n *\n * @example Minimal example\n * ```ts\n * // sanity.config.ts\n * import { defineConfig } from 'sanity'\n * import { linkField } from 'sanity-plugin-link-field'\n *\n * export default defineConfig((\n * // ...\n * plugins: [\n * linkField()\n * ]\n * })\n *\n * // mySchema.ts\n * import { defineField, defineType } from 'sanity';\n *\n * export const mySchema = defineType({\n * // ...\n * fields: [\n * // ...\n * defineField({\n * name: 'link',\n * title: 'Link',\n * type: 'link'\n * }),\n * ]\n *});\n * ```\n */\nexport const linkField = definePlugin<LinkFieldPluginOptions | void>((opts) => {\n const {\n linkableSchemaTypes = ['page'],\n weakReferences = false,\n referenceFilterOptions,\n descriptions = {\n internal: 'Link to another page or document on the website.',\n external: 'Link to an absolute URL to a page on another website.',\n email: 'Link to send an e-mail to the given address.',\n phone: 'Link to call the given phone number.',\n advanced: 'Optional. Add anchor links and custom parameters.',\n parameters: 'Optional. Add custom parameters to the URL, such as UTM tags.',\n anchor: 'Optional. Add an anchor to link to a specific section on the page.',\n },\n enableLinkParameters = true,\n enableAnchorLinks = true,\n customLinkTypes = [],\n icon,\n preview,\n } = opts || {}\n\n const linkType = defineType({\n name: 'link',\n title: 'Link',\n type: 'object',\n icon,\n preview,\n fieldsets: [\n {\n name: 'advanced',\n title: 'Advanced',\n description: descriptions.advanced,\n options: {\n collapsible: true,\n collapsed: true,\n },\n },\n ],\n fields: [\n defineField({\n name: 'text',\n type: 'string',\n description: descriptions.text,\n }),\n\n defineField({\n name: 'type',\n type: 'string',\n initialValue: 'internal',\n validation: (Rule) => Rule.required(),\n components: {\n input: (props) => (\n <LinkTypeInput\n customLinkTypes={customLinkTypes}\n linkableSchemaTypes={linkableSchemaTypes}\n {...props}\n />\n ),\n },\n }),\n\n // Internal\n defineField({\n name: 'internalLink',\n type: 'reference',\n to: linkableSchemaTypes.map((type) => ({\n type,\n })),\n weak: weakReferences,\n options: {\n disableNew: true,\n ...referenceFilterOptions,\n },\n description: descriptions?.internal,\n hidden: ({parent}) => !!parent?.type && parent?.type !== 'internal',\n }),\n\n // External\n defineField({\n name: 'url',\n type: 'url',\n description: descriptions?.external,\n validation: (rule) =>\n rule.uri({\n allowRelative: true,\n scheme: ['https', 'http'],\n }),\n hidden: ({parent}) => parent?.type !== 'external',\n }),\n\n // E-mail\n defineField({\n name: 'email',\n type: 'email',\n description: descriptions?.email,\n hidden: ({parent}) => parent?.type !== 'email',\n }),\n\n // Phone\n defineField({\n name: 'phone',\n type: 'string',\n description: descriptions?.phone,\n validation: (rule) =>\n rule.custom((value, context) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!value || (context.parent as any)?.type !== 'phone') {\n return true\n }\n\n return (\n (new RegExp(/^\\+?[0-9\\s-]*$/).test(value) &&\n !value.startsWith('-') &&\n !value.endsWith('-')) ||\n 'Must be a valid phone number'\n )\n }),\n hidden: ({parent}) => parent?.type !== 'phone',\n }),\n\n // Custom\n defineField({\n name: 'value',\n type: 'string',\n description: descriptions?.external,\n hidden: ({parent}) => !parent || !isCustomLink(parent as LinkValue),\n components: {\n input: (props) => <CustomLinkInput customLinkTypes={customLinkTypes} {...props} />,\n },\n }),\n\n // New tab\n defineField({\n title: 'Open in new window',\n name: 'blank',\n type: 'boolean',\n initialValue: false,\n description: descriptions.blank,\n hidden: ({parent}) => parent?.type === 'email' || parent?.type === 'phone',\n }),\n\n // Parameters\n ...(enableLinkParameters || enableAnchorLinks\n ? [\n ...(enableLinkParameters\n ? [\n defineField({\n title: 'Parameters',\n name: 'parameters',\n type: 'string',\n description: descriptions.parameters,\n validation: (rule) =>\n rule.custom((value, context) => {\n if (\n !value ||\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (context.parent as any)?.type === 'email' ||\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (context.parent as any)?.type === 'phone'\n ) {\n return true\n }\n\n if (value.indexOf('?') !== 0) {\n return 'Must start with ?; eg. ?utm_source=example.com&utm_medium=referral'\n }\n\n if (value.length === 1) {\n return 'Must contain at least one parameter'\n }\n\n return true\n }),\n hidden: ({parent}) => parent?.type === 'email' || parent?.type === 'phone',\n fieldset: 'advanced',\n }),\n ]\n : []),\n\n // Anchor\n ...(enableAnchorLinks\n ? [\n defineField({\n title: 'Anchor',\n name: 'anchor',\n type: 'string',\n description: descriptions.anchor,\n validation: (rule) =>\n rule.custom((value, context) => {\n if (\n !value ||\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (context.parent as any)?.type === 'email' ||\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (context.parent as any)?.type === 'phone'\n ) {\n return true\n }\n\n if (value.indexOf('#') !== 0) {\n return 'Must start with #; eg. #page-section-1'\n }\n\n if (value.length === 1) {\n return 'Must contain at least one character'\n }\n\n return (\n new RegExp(/^([-?/:@._~!$&'()*+,;=a-zA-Z0-9]|%[0-9a-fA-F]{2})*$/).test(\n value.replace(/^#/, ''),\n ) || 'Invalid URL fragment'\n )\n }),\n hidden: ({parent}) => parent?.type === 'email' || parent?.type === 'phone',\n fieldset: 'advanced',\n }),\n ]\n : []),\n ]\n : []),\n ],\n components: {\n input: (props: ObjectInputProps) => (\n <LinkInput\n customLinkTypes={customLinkTypes}\n {...(props as ObjectInputProps<LinkValue, LinkSchemaType>)}\n />\n ),\n },\n })\n\n return {\n name: 'link-field',\n schema: {\n types: [linkType],\n },\n }\n})\n"],"names":["isInternalLink","isExternalLink","isEmailLink","isPhoneLink","isCustomLink","memo","useWorkspace","useFormValue","useState","useEffect","options","jsx","Select","set","jsxs","Fragment","Spinner","linkField","Stack","ObjectInputMember","Text","Flex","Box","FormFieldValidationStatus","LinkIcon","GlobeIcon","AtSignIcon","PhoneIcon","value","MenuButton","Button","ChevronDownIcon","Menu","MenuItem","definePlugin","defineType","defineField"],"mappings":";;;AAQO,MAAM,oBAAoB,CAAC,UAA0C;AAC1E,QAAM,OAAO;AAEb,SAAI,CAAC,QAAQ,CAAC,KAAK,OACV,qBAGLA,QAAAA,eAAe,IAAI,KAAK,CAAC,KAAK,eACzB;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,EAAA,IAINC,QAAAA,eAAe,IAAI,KAAK,CAAC,KAAK,MACzB;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,EAAA,IAINC,QAAAA,YAAY,IAAI,KAAK,CAAC,KAAK,QACtB;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,EAAA,IAINC,QAAAA,YAAY,IAAI,KAAK,CAAC,KAAK,QACtB;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,EAAA,IAINC,QAAAA,aAAa,IAAI,KAAK,CAAC,KAAK,QACvB;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,EAAA,IAIH;AACT,GCzCa,kBAAkBC,MAAAA,KAAK,SAClC,OAGA;AACA,QAAM,YAAYC,OAAAA,aAAA,GACZ,WAAWC,OAAAA,aAAa,CAAA,CAAE,GAC1B,YAAYA,OAAAA,aAAa,MAAM,KAAK,MAAM,GAAG,EAAE,CAAC,GAChD,CAAC,SAAS,UAAU,IAAIC,MAAAA,SAAyC,IAAI,GAErE,iBAAiB,MAAM,gBAAgB,KAAK,CAAC,SAAS,KAAK,UAAU,UAAW,IAAI;AAE1F,SAAAC,MAAAA,UAAU,MAAM;AACV,uBACE,MAAM,QAAQ,gBAAgB,OAAO,IACvC,WAAW,eAAe,OAAO,IAEjC,eACG,QAAQ,UAAU,MAAM,MAAM,UAAU,WAAW,EACnD,KAAK,CAACC,aAAY,WAAWA,QAAO,CAAC;AAAA,EAI9C,GAAG,CAAC,gBAAgB,MAAM,MAAM,UAAU,WAAW,CAAC,GAE/C,UACLC,2BAAAA;AAAAA,IAACC,GAAAA;AAAAA,IAAA;AAAA,MACC,UAAU,CAAC,MAAM;AACf,cAAM,SAASC,WAAI,EAAE,cAAc,SAAS,EAAE,CAAC;AAAA,MACjD;AAAA,MAEA,UAAAC,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,QAAAJ,2BAAAA,IAAC,UAAA,EAAO,OAAM,IAAG,UAAU,MAAM,UAAU,IAAI,UAAQ,IAAC,QAAM,GAAA,CAAC;AAAA,QAC9D,QAAQ,IAAI,CAAC,WACZA,2BAAAA,IAAC,UAAA,EAA0B,OAAO,OAAO,OAAO,UAAU,MAAM,UAAU,OAAO,OAC9E,iBAAO,MAAA,GADG,OAAO,KAEpB,CACD;AAAA,MAAA,EAAA,CACH;AAAA,IAAA;AAAA,EAAA,IAGFA,2BAAAA,IAACK,GAAAA,SAAA,EAAQ,OAAO,EAAC,YAAY,YAAW;AAE5C,CAAC,GCvCY,YAAYX,MAAAA,KAAK,SAAmB,OAAuB;AACtE,QAAM,CAAC,WAAW,WAAWY,YAAW,GAAG,WAAW,IAAI,MAAM,SAC1D,EAAC,YAAW,MAAM,YAElB;AAAA,IACJ,OAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY,EAAC,aAAa,qBAAA;AAAA,IAAoB;AAAA,EAChD,IACEA,YAEE;AAAA;AAAA,IAEJ,MAAM,SAASb,QAAAA,aAAa,MAAM,KAAK,IACnC,MAAM,gBAAgB,KAAK,CAAC,SAAS,KAAK,UAAU,MAAM,OAAO,IAAI,GAAG;AAAA;AAAA,MAExE;AAAA;AAAA,KAEA,cAAc;AAAA,IAClB,kBAAkB,MAAM;AAAA,IACxB,aAAa,MAAM;AAAA,IACnB,aAAa,MAAM;AAAA,IACnB,mBAAmB,MAAM;AAAA,IACzB,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM;AAAA,IAClB,eAAe,MAAM;AAAA,EAAA;AAGvB,SACEU,2BAAAA,KAACI,GAAAA,OAAA,EAAM,OAAO,GAEX,UAAA;AAAA,IAAA,SAAS,cACRP,2BAAAA;AAAAA,MAACQ,OAAAA;AAAAA,MAAA;AAAA,QACC,QAAQ;AAAA,UACN,GAAG;AAAA,UACH,OAAO;AAAA,YACL,GAAG,UAAU;AAAA,YACb,YAAY;AAAA,cACV,GAAG,UAAU,MAAM;AAAA,cACnB,OAAO,SAAS,aAAa,UAAU,MAAM,WAAW;AAAA,YAAA;AAAA,UAC1D;AAAA,QACF;AAAA,QAED,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,IAIRL,2BAAAA,KAACI,GAAAA,OAAA,EAAM,OAAO,GAGX,UAAA;AAAA,MAAA,SAAS,6CACPE,GAAAA,MAAA,EAAK,IAAG,SAAQ,QAAO,UAAS,MAAM,GAAG,UAAA,OAAA,CAE1C;AAAA,MAGFN,2BAAAA,KAACO,GAAAA,MAAA,EAAK,KAAK,GAAG,OAAM,cAElB,UAAA;AAAA,QAAAV,2BAAAA;AAAAA,UAACQ,OAAAA;AAAAA,UAAA;AAAA,YACC,QAAQ;AAAA,cACN,GAAG;AAAA,cACH,OAAO;AAAA,gBACL,GAAG,UAAU;AAAA,gBACb,YAAY;AAAA,kBACV,GAAG,UAAU,MAAM;AAAA,kBACnB,OAAO;AAAA,gBAAA;AAAA,cACT;AAAA,YACF;AAAA,YAED,GAAG;AAAA,UAAA;AAAA,QAAA;AAAA,QAGNL,gCAACI,GAAAA,SAAM,OAAO,GAAG,OAAO,EAAC,OAAO,UAE9B,UAAA;AAAA,UAAAP,2BAAAA;AAAAA,YAACQ,OAAAA;AAAAA,YAAA;AAAA,cACC,QAAQ;AAAA,gBACN,GAAGF;AAAA,gBACH,OAAO;AAAA,kBACL,GAAGA,WAAU;AAAA,kBACb,YAAY;AAAA,oBACV,GAAGA,WAAU,MAAM;AAAA,oBACnB,OAAO;AAAA,kBAAA;AAAA,gBACT;AAAA,cACF;AAAA,cAED,GAAG;AAAA,YAAA;AAAA,UAAA;AAAA,UAIL,oBAAoB,SAAS,KAC5BN,2BAAAA;AAAAA,YAACW,GAAAA;AAAAA,YAAA;AAAA,cACC,OAAO;AAAA,gBACL,SAAS;AAAA,gBACT,cAAc;AAAA,gBACd,YAAY;AAAA,gBACZ,aAAa;AAAA,cAAA;AAAA,cAGf,UAAAX,2BAAAA;AAAAA,gBAACY,OAAAA;AAAAA,gBAAA;AAAA,kBACC,UAAU;AAAA,kBACV,WAAU;AAAA,kBACV,YAAY;AAAA,gBAAA;AAAA,cAAA;AAAA,YACd;AAAA,UAAA;AAAA,QACF,EAAA,CAEJ;AAAA,MAAA,GACF;AAAA,MAGC,eACCZ,2BAAAA,IAACS,GAAAA,MAAA,EAAK,OAAK,IAAC,MAAM,GACf,UAAA,YAAA,CACH;AAAA,IAAA,GAEJ;AAAA,IAGC,YAAY,IAAI,CAAC,UAChBT,2BAAAA,IAACQ,OAAAA,mBAAA,EAAkC,QAAQ,OAAQ,GAAG,eAA9B,MAAM,GAAqC,CACpE;AAAA,EAAA,GACH;AAEJ,CAAC,GChIK,YAAY,IAEZ,mBAA+B;AAAA,EACnC,EAAC,OAAO,YAAY,OAAO,YAAY,MAAMK,YAAAA,SAAA;AAAA,EAC7C,EAAC,OAAO,OAAO,OAAO,YAAY,MAAMC,YAAAA,UAAA;AAAA,EACxC,EAAC,OAAO,SAAS,OAAO,SAAS,MAAMC,YAAAA,WAAA;AAAA,EACvC,EAAC,OAAO,SAAS,OAAO,SAAS,MAAMC,YAAAA,UAAA;AACzC;AAKA,SAAS,aAAa,MAA8B;AAClD,SAAO,iBAAiB,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AACrD;AAKA,SAAS,gBAAgB,MAAiC;AACxD,WAAS,UAAU,OAAgC;AACjD,WAAOhB,2BAAAA,IAAC,MAAA,EAAK,MAAM,WAAY,GAAG,OAAO;AAAA,EAC3C;AACA,SAAA,UAAU,cAAc,aAAa,KAAK,eAAe,KAAK,QAAQ,SAAS,KACxE;AACT;AAKA,SAAS,QAAQ,MAA+B;AAC9C,SAAI,aAAa,KAAK,IAAI,IACjB,gBAAgB,KAAK,IAAkB,IAEzC,KAAK;AACd;AAMO,MAAM,gBAAgBN,MAAAA,KAAK,SAAuB;AAAA,EACvD;AAAA,EACA;AAAA,EACA,kBAAkB,CAAA;AAAA,EAClB;AACF,GAGG;AACD,QAAM,YAAY;AAAA;AAAA,IAEhB,GAAG,iBAAiB;AAAA,MAClB,CAAC,EAAC,OAAAuB,OAAAA,MAAWA,WAAU,cAAc,qBAAqB,SAAS;AAAA,IAAA;AAAA,IAErE,GAAG;AAAA,EAAA,GAGC,eAAe,UAAU,KAAK,CAAC,SAAS,KAAK,UAAU,KAAK,KAAK,UAAU,CAAC;AAElF,SACEjB,2BAAAA;AAAAA,IAACkB,GAAAA;AAAAA,IAAA;AAAA,MACC,QACElB,2BAAAA;AAAAA,QAACmB,GAAAA;AAAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,MAAM,QAAQ,YAAY;AAAA,UAC1B,WAAWC,MAAAA;AAAAA,UACX,OAAM;AAAA,UACN,cAAY,gCAAgC,aAAa,KAAK;AAAA,UAC9D,OAAO,EAAC,QAAQ,OAAA;AAAA,QAAM;AAAA,MAAA;AAAA,MAG1B,IAAG;AAAA,MACH,MACEpB,2BAAAA,IAACqB,GAAAA,MAAA,EACE,UAAA,UAAU,IAAI,CAAC,SACdrB,2BAAAA;AAAAA,QAACsB,GAAAA;AAAAA,QAAA;AAAA,UAEC,MAAM,KAAK;AAAA,UACX,MAAM,QAAQ,IAAI;AAAA,UAClB,SAAS,MAAM;AACb,qBAASpB,OAAAA,IAAI,KAAK,KAAK,CAAC;AAAA,UAC1B;AAAA,QAAA;AAAA,QALK,KAAK;AAAA,MAAA,CAOb,EAAA,CACH;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC,GCvDY,YAAYqB,OAAAA,aAA4C,CAAC,SAAS;AAC7E,QAAM;AAAA,IACJ,sBAAsB,CAAC,MAAM;AAAA,IAC7B,iBAAiB;AAAA,IACjB;AAAA,IACA,eAAe;AAAA,MACb,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO;AAAA,MACP,OAAO;AAAA,MACP,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,QAAQ;AAAA,IAAA;AAAA,IAEV,uBAAuB;AAAA,IACvB,oBAAoB;AAAA,IACpB,kBAAkB,CAAA;AAAA,IAClB;AAAA,IACA;AAAA,EAAA,IACE,QAAQ,CAAA;AAoNZ,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,OAAO,CArNMC,OAAAA,WAAW;AAAA,QAC1B,MAAM;AAAA,QACN,OAAO;AAAA,QACP,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,WAAW;AAAA,UACT;AAAA,YACE,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa,aAAa;AAAA,YAC1B,SAAS;AAAA,cACP,aAAa;AAAA,cACb,WAAW;AAAA,YAAA;AAAA,UACb;AAAA,QACF;AAAA,QAEF,QAAQ;AAAA,UACNC,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,aAAa,aAAa;AAAA,UAAA,CAC3B;AAAA,UAEDA,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,cAAc;AAAA,YACd,YAAY,CAAC,SAAS,KAAK,SAAA;AAAA,YAC3B,YAAY;AAAA,cACV,OAAO,CAAC,UACNzB,2BAAAA;AAAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC;AAAA,kBACA;AAAA,kBACC,GAAG;AAAA,gBAAA;AAAA,cAAA;AAAA,YACN;AAAA,UAEJ,CACD;AAAA;AAAA,UAGDyB,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,IAAI,oBAAoB,IAAI,CAAC,UAAU;AAAA,cACrC;AAAA,YAAA,EACA;AAAA,YACF,MAAM;AAAA,YACN,SAAS;AAAA,cACP,YAAY;AAAA,cACZ,GAAG;AAAA,YAAA;AAAA,YAEL,aAAa,cAAc;AAAA,YAC3B,QAAQ,CAAC,EAAC,aAAY,CAAC,CAAC,QAAQ,QAAQ,QAAQ,SAAS;AAAA,UAAA,CAC1D;AAAA;AAAA,UAGDA,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,aAAa,cAAc;AAAA,YAC3B,YAAY,CAAC,SACX,KAAK,IAAI;AAAA,cACP,eAAe;AAAA,cACf,QAAQ,CAAC,SAAS,MAAM;AAAA,YAAA,CACzB;AAAA,YACH,QAAQ,CAAC,EAAC,aAAY,QAAQ,SAAS;AAAA,UAAA,CACxC;AAAA;AAAA,UAGDA,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,aAAa,cAAc;AAAA,YAC3B,QAAQ,CAAC,EAAC,aAAY,QAAQ,SAAS;AAAA,UAAA,CACxC;AAAA;AAAA,UAGDA,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,aAAa,cAAc;AAAA,YAC3B,YAAY,CAAC,SACX,KAAK,OAAO,CAAC,OAAO,YAEd,CAAC,SAAU,QAAQ,QAAgB,SAAS,UACvC,KAIN,IAAI,OAAO,gBAAgB,EAAE,KAAK,KAAK,KACtC,CAAC,MAAM,WAAW,GAAG,KACrB,CAAC,MAAM,SAAS,GAAG,KACrB,8BAEH;AAAA,YACH,QAAQ,CAAC,EAAC,aAAY,QAAQ,SAAS;AAAA,UAAA,CACxC;AAAA;AAAA,UAGDA,mBAAY;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,aAAa,cAAc;AAAA,YAC3B,QAAQ,CAAC,EAAC,OAAA,MAAY,CAAC,UAAU,CAAChC,QAAAA,aAAa,MAAmB;AAAA,YAClE,YAAY;AAAA,cACV,OAAO,CAAC,yCAAW,iBAAA,EAAgB,iBAAmC,GAAG,MAAA,CAAO;AAAA,YAAA;AAAA,UAClF,CACD;AAAA;AAAA,UAGDgC,mBAAY;AAAA,YACV,OAAO;AAAA,YACP,MAAM;AAAA,YACN,MAAM;AAAA,YACN,cAAc;AAAA,YACd,aAAa,aAAa;AAAA,YAC1B,QAAQ,CAAC,EAAC,aAAY,QAAQ,SAAS,WAAW,QAAQ,SAAS;AAAA,UAAA,CACpE;AAAA;AAAA,UAGD,GAAI,wBAAwB,oBACxB;AAAA,YACE,GAAI,uBACA;AAAA,cACEA,mBAAY;AAAA,gBACV,OAAO;AAAA,gBACP,MAAM;AAAA,gBACN,MAAM;AAAA,gBACN,aAAa,aAAa;AAAA,gBAC1B,YAAY,CAAC,SACX,KAAK,OAAO,CAAC,OAAO,YAEhB,CAAC;AAAA,gBAEA,QAAQ,QAAgB,SAAS;AAAA,gBAEjC,QAAQ,QAAgB,SAAS,UAE3B,KAGL,MAAM,QAAQ,GAAG,MAAM,IAClB,uEAGL,MAAM,WAAW,IACZ,wCAGF,EACR;AAAA,gBACH,QAAQ,CAAC,EAAC,aAAY,QAAQ,SAAS,WAAW,QAAQ,SAAS;AAAA,gBACnE,UAAU;AAAA,cAAA,CACX;AAAA,YAAA,IAEH,CAAA;AAAA;AAAA,YAGJ,GAAI,oBACA;AAAA,cACEA,mBAAY;AAAA,gBACV,OAAO;AAAA,gBACP,MAAM;AAAA,gBACN,MAAM;AAAA,gBACN,aAAa,aAAa;AAAA,gBAC1B,YAAY,CAAC,SACX,KAAK,OAAO,CAAC,OAAO,YAEhB,CAAC;AAAA,gBAEA,QAAQ,QAAgB,SAAS;AAAA,gBAEjC,QAAQ,QAAgB,SAAS,UAE3B,KAGL,MAAM,QAAQ,GAAG,MAAM,IAClB,2CAGL,MAAM,WAAW,IACZ,wCAIP,IAAI,OAAO,qDAAqD,EAAE;AAAA,kBAChE,MAAM,QAAQ,MAAM,EAAE;AAAA,gBAAA,KACnB,sBAER;AAAA,gBACH,QAAQ,CAAC,EAAC,aAAY,QAAQ,SAAS,WAAW,QAAQ,SAAS;AAAA,gBACnE,UAAU;AAAA,cAAA,CACX;AAAA,YAAA,IAEH,CAAA;AAAA,UAAC,IAEP,CAAA;AAAA,QAAC;AAAA,QAEP,YAAY;AAAA,UACV,OAAO,CAAC,UACNzB,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC;AAAA,cACC,GAAI;AAAA,YAAA;AAAA,UAAA;AAAA,QACP;AAAA,MAEJ,CACD,CAKmB;AAAA,IAAA;AAAA,EAClB;AAEJ,CAAC;;;;;;;;"}