@hanzo/ui 2.0.5 → 2.5.0

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 (81) hide show
  1. package/assets/lux-site-icons/android-chrome-192x192.png +0 -0
  2. package/assets/lux-site-icons/android-chrome-512x512.png +0 -0
  3. package/assets/lux-site-icons/apple-touch-icon.png +0 -0
  4. package/assets/lux-site-icons/favicon-16x16.png +0 -0
  5. package/assets/lux-site-icons/favicon-32x32.png +0 -0
  6. package/assets/lux-site-icons/favicon.ico +0 -0
  7. package/assets/standard-docs/LUX-NFT-Terms-and-Conditions.pdf +0 -0
  8. package/assets/standard-docs/LUX-Privacy-Policy.pdf +0 -0
  9. package/blocks/components/card-block.tsx +1 -1
  10. package/blocks/components/screenful-block/index.tsx +11 -1
  11. package/common/chat-widget.tsx +75 -0
  12. package/common/contact-dialog/contact-form.tsx +111 -0
  13. package/common/contact-dialog/disclaimer.tsx +13 -0
  14. package/common/contact-dialog/index.tsx +48 -0
  15. package/common/copyright.tsx +21 -0
  16. package/common/drawer-menu.tsx +54 -0
  17. package/common/footer.tsx +77 -0
  18. package/common/head-metadata/from-next/metadata-types.ts +158 -0
  19. package/common/head-metadata/from-next/opengraph-types.ts +267 -0
  20. package/common/head-metadata/from-next/twitter-types.ts +92 -0
  21. package/common/head-metadata/index.tsx +208 -0
  22. package/common/header/index.tsx +66 -0
  23. package/common/header/mobile-nav.tsx +72 -0
  24. package/common/header/theme-toggle.tsx +26 -0
  25. package/common/icons/index.tsx +34 -0
  26. package/common/icons/lux-logo.tsx +10 -0
  27. package/common/icons/secure-delivery.tsx +13 -0
  28. package/common/icons/social-icon.tsx +35 -0
  29. package/common/index.ts +14 -0
  30. package/common/logo.tsx +71 -0
  31. package/common/mini-chart/index.tsx +8 -0
  32. package/common/mini-chart/mini-chart-props.ts +44 -0
  33. package/common/mini-chart/mini-chart.tsx +76 -0
  34. package/common/mini-chart/wrapper.tsx +23 -0
  35. package/context-providers/index.ts +1 -0
  36. package/{style → context-providers}/theme-provider.tsx +2 -2
  37. package/next/README.md +11 -0
  38. package/next/analytics/fpixel.ts +18 -0
  39. package/next/analytics/pixel-analytics.tsx +55 -0
  40. package/next/determine-device-middleware.ts +16 -0
  41. package/next/fonts/DrukTextWide-Bold-Trial.otf +0 -0
  42. package/next/fonts/DrukTextWide-Heavy-Trial.otf +0 -0
  43. package/next/fonts/DrukTextWide-Medium-Trial.otf +0 -0
  44. package/next/fonts/DrukWide-Bold-Trial.otf +0 -0
  45. package/next/fonts/DrukWide-Heavy-Trial.otf +0 -0
  46. package/next/fonts/DrukWide-Medium-Trial.otf +0 -0
  47. package/next/get-app-router-font-classes.ts +12 -0
  48. package/next/load-and-return-lux-next-fonts-on-import.ts +94 -0
  49. package/next/next-font-desc.ts +28 -0
  50. package/next/not-found-content.mdx +5 -0
  51. package/next/not-found.tsx +23 -0
  52. package/next/pages-router-font-vars.tsx +18 -0
  53. package/next/root-layout.tsx +62 -0
  54. package/package.json +12 -10
  55. package/primitives/carousel.tsx +263 -0
  56. package/primitives/index.ts +8 -1
  57. package/primitives/toggle-group.tsx +1 -1
  58. package/primitives/youtube-embed.tsx +1 -1
  59. package/siteDef/footer/community.tsx +67 -0
  60. package/siteDef/footer/company.ts +37 -0
  61. package/siteDef/footer/ecosystem.ts +37 -0
  62. package/siteDef/footer/index.tsx +26 -0
  63. package/siteDef/footer/legal.ts +28 -0
  64. package/siteDef/footer/network.ts +37 -0
  65. package/siteDef/footer/svg/warpcast-logo.svg +12 -0
  66. package/siteDef/main-nav.ts +35 -0
  67. package/style/hanzo-default-colors.css +2 -2
  68. package/style/social-svg.css +3 -0
  69. package/tailwind/{fontSize.tailwind.ts → fonts.tailwind.ts} +19 -1
  70. package/tailwind/index.ts +15 -4
  71. package/tailwind/lux-tw-fonts.ts +37 -0
  72. package/tailwind/{tailwind.config.hanzo-preset.js → tailwind.config.base.js} +2 -4
  73. package/tailwind/typo-plugin/get-plugin-styles.js +42 -42
  74. package/tailwind/typography-test.mdx +1 -1
  75. package/types/index.ts +15 -5
  76. package/types/site-def.ts +36 -0
  77. package/primitives/icons/index.ts +0 -18
  78. package/tailwind/fontFamily.tailwind.ts +0 -7
  79. package/tailwind/tailwind.config.hanzo-preset.d.ts +0 -5
  80. /package/{primitives → common}/icons/github.tsx +0 -0
  81. /package/{primitives → common}/icons/youtube-logo.tsx +0 -0
@@ -14,7 +14,7 @@ import {
14
14
  type TypographySize
15
15
  } from '../../primitives'
16
16
 
17
- import { Icons } from '../../primitives'
17
+ import { Icons } from '../../common'
18
18
 
19
19
  import {
20
20
  getSpecifierData,
@@ -36,6 +36,8 @@ const ScreenfulComponent: React.FC<{
36
36
 
37
37
  const specified = (s: string) => (containsToken(b.specifiers, s))
38
38
  const narrowGutters = specified('narrow-gutters') // eg, for a table object that is large
39
+ const noGutters = specified('no-gutters')
40
+ const vertCenter = specified('vert-center')
39
41
 
40
42
  // content wrapper clx:
41
43
  // [
@@ -49,6 +51,8 @@ const ScreenfulComponent: React.FC<{
49
51
  // mobile header: 44px / pt-11
50
52
  narrowGutters ?
51
53
  'px-6 lg:px-8 2xl:px-2 pb-6 ' + (snapTile ? 'pt-15 md:pt-26 lg:pt-28 ' : '') // otherwise assume there is a Main
54
+ : noGutters ?
55
+ 'px-0 pb-0 ' + (snapTile ? 'pt-11 lg:pt-20 ' : '') // otherwise assume there is a Main
52
56
  :
53
57
  'px-[8vw] xl:px-[1vw] pb-[8vh] pt-[calc(44px+4vh)] md:pt-[calc(80px+6vh)] ',
54
58
 
@@ -68,7 +72,13 @@ const ScreenfulComponent: React.FC<{
68
72
  initialInView={initialInView}
69
73
  />
70
74
  )}
71
- <div className={cn(...cwclx, snapTile ? 'absolute left-0 right-0 top-0 bottom-0' : 'flex min-h-screen w-full', contentClx)} >
75
+ <div className={cn(
76
+ ...cwclx,
77
+ snapTile ? 'absolute left-0 right-0 top-0 bottom-0' : 'flex min-h-screen w-full',
78
+ contentClx,
79
+ vertCenter ? '!py-0 self-center' : ''
80
+ )}
81
+ >
72
82
  <Content block={b} agent={agent} className='w-full'/>
73
83
  {b.footer}
74
84
  </div>
@@ -0,0 +1,75 @@
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import { X } from 'lucide-react'
5
+
6
+ import LuxLogo from './icons/lux-logo'
7
+ import { Button, Card } from '../primitives'
8
+
9
+ const ChatWidget: React.FC<{
10
+ title: string,
11
+ chatbotUrl: string,
12
+ subtitle?: string,
13
+ question?: string,
14
+ /*
15
+ Currently supports these icons from remix icons (https://remixicon.com/):
16
+ GlobalLineIcon,
17
+ ShieldFlashLineIcon,
18
+ BankCardLineIcon,
19
+ GroupLineIcon,
20
+ QuestionnaireLineIcon
21
+ */
22
+ suggestedQuestions?: { heading: string, message: string, icon?: string }[]
23
+ }> = ({
24
+ title,
25
+ chatbotUrl,
26
+ subtitle,
27
+ question,
28
+ suggestedQuestions
29
+ }) => {
30
+
31
+ const [showChatbot, setShowChatbot] = React.useState<boolean>(false)
32
+
33
+ const onClick = () => { setShowChatbot(!showChatbot) }
34
+
35
+ const searchParams = new URLSearchParams()
36
+ if (question) {
37
+ searchParams.append('question', question)
38
+ }
39
+ if (suggestedQuestions) {
40
+ searchParams.append('sQuestions', suggestedQuestions.map(({ message }) => message).join(','))
41
+ searchParams.append('sHeadings', suggestedQuestions.map(({ heading }) => heading).join(','))
42
+ searchParams.append('sIcons', suggestedQuestions.map(({ icon }) => icon).join(','))
43
+ }
44
+
45
+ const iframeSrc = `${chatbotUrl}?${searchParams.toString()}`
46
+
47
+ return (<>
48
+ <div className={
49
+ 'fixed bottom-0 sm:bottom-20 right-0 w-full h-full ' +
50
+ 'sm:max-w-[400px] sm:max-h-[550px] sm:px-4 z-[1002] ' +
51
+ (showChatbot ? 'flex' : 'hidden')
52
+ }>
53
+ <Card className='flex flex-col h-full w-full'>
54
+ <div className='flex px-4 py-2 h-12 bg-level-0 items-center justify-between'>
55
+ <h3 className='font-semibold font-heading'>{title} <span className='opacity-60'>{subtitle}</span></h3>
56
+ <Button onClick={onClick} variant='link' size='icon' className='w-fit sm:hidden'>
57
+ <X />
58
+ </Button>
59
+ </div>
60
+ <iframe src={iframeSrc} className='h-full' />
61
+ </Card>
62
+ </div>
63
+
64
+ <Button
65
+ variant='outline'
66
+ size='link'
67
+ onClick={onClick}
68
+ className='fixed bottom-4 right-0 h-12 w-12 mx-4 rounded-full z-[1001]'
69
+ >
70
+ {showChatbot ? <X /> : <LuxLogo width={24} height={24} className='mt-2' />}
71
+ </Button>
72
+ </>)
73
+ }
74
+
75
+ export default ChatWidget
@@ -0,0 +1,111 @@
1
+ 'use client'
2
+
3
+ import React, { useTransition } from 'react'
4
+
5
+ import { zodResolver } from '@hookform/resolvers/zod'
6
+ import { useForm, type SubmitHandler, type ControllerRenderProps } from 'react-hook-form'
7
+ import * as z from 'zod'
8
+ // @ts-ignore
9
+ import validator from 'validator'
10
+
11
+ import { Loader2 } from 'lucide-react'
12
+
13
+ import {
14
+ Button,
15
+ Input,
16
+ Form,
17
+ FormControl,
18
+ FormField,
19
+ FormItem,
20
+ FormMessage,
21
+ } from '../../primitives'
22
+
23
+ import type { ContactInfo, SubmitServerAction } from '../../types'
24
+
25
+ const ValidationSchema = z.object({
26
+ email: z
27
+ .string()
28
+ .min(1, { message: "Email must be provided." })
29
+ .email("Invalid email."),
30
+ phone: z
31
+ .string()
32
+ .min(1, { message: "Telephone must be provided." })
33
+ .refine(validator.isMobilePhone, { message: "Invalid format." })
34
+ })
35
+
36
+ const ContactForm: React.FC<{
37
+ onSubmit: SubmitServerAction
38
+ enclosure: any
39
+ }> = ({
40
+ onSubmit,
41
+ enclosure
42
+ }) => {
43
+
44
+ const form = useForm<ContactInfo>({
45
+ // @ts-ignore (pnpm linking / tsc bug )
46
+ resolver: zodResolver(ValidationSchema),
47
+ defaultValues: {
48
+ email: '',
49
+ phone: '',
50
+ },
51
+ })
52
+
53
+ const [isPending, startTransition] = useTransition()
54
+
55
+ const onFormSubmit: SubmitHandler<ContactInfo> = (data) => {
56
+ // https://github.com/orgs/react-hook-form/discussions/10757#discussioncomment-6672403
57
+ // @ts-ignore
58
+ startTransition(async () => {
59
+ await onSubmit(data, enclosure)
60
+ })
61
+ }
62
+
63
+ const MyFormItem: React.FC<{
64
+ field: ControllerRenderProps<ContactInfo, 'email'> | ControllerRenderProps<ContactInfo, 'phone'>
65
+ placeholder: string
66
+ }> = ({
67
+ field,
68
+ placeholder
69
+ }) => (
70
+ <FormItem className="space-y-0" >
71
+ <FormControl>
72
+ <Input placeholder={placeholder} {...field} className="mt-0 text-foreground"/>
73
+ </FormControl>
74
+ <div className="flex flex-row justify-start items-stretch gap-2">
75
+ <FormMessage />
76
+ </div>
77
+ </FormItem>
78
+ )
79
+
80
+ return (
81
+ <Form {...form}>
82
+ <form onSubmit={form.handleSubmit(onFormSubmit)} className="w-3/4">
83
+ <div className='flex flex-col justify-start items-stretch mt-4'>
84
+ <FormField
85
+ control={form.control}
86
+ name='email'
87
+ // @ts-ignore
88
+ render={({ field }) => ( <MyFormItem field={field} placeholder='email'/> )}
89
+ />
90
+ <FormField
91
+ control={form.control}
92
+ name='phone'
93
+ // @ts-ignore
94
+ render={({ field }) => ( <MyFormItem field={field} placeholder='phone'/> )}
95
+ />
96
+ <Button disabled={isPending} type='submit' className='bg-primary text-primary-fg hover:bg-primary-hover'>
97
+ {isPending ? (<>
98
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
99
+ Please wait
100
+ </>
101
+ ) : (
102
+ <>Submit</>
103
+ )}
104
+ </Button>
105
+ </div>
106
+ </form>
107
+ </Form>
108
+ )
109
+ }
110
+
111
+ export default ContactForm
@@ -0,0 +1,13 @@
1
+ import React from 'react'
2
+
3
+ const Disclaimer: React.FC = () => (
4
+ <div>
5
+ By entering your mobile number and submitting, you consent to receive text messages from Lux at the number provided.
6
+ Message and data rates may apply. Message frequency varies.
7
+ You can unsubscribe at any time by replying STOP.
8
+ View our <a href='/privacy'>Privacy Policy</a> and <a href='/terms'>Terms & conditions</a>.
9
+ </div>
10
+ )
11
+
12
+ export default Disclaimer
13
+
@@ -0,0 +1,48 @@
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import type { ButtonModalProps} from '../../types'
5
+
6
+ import {
7
+ Button,
8
+ Dialog,
9
+ DialogContent,
10
+ DialogDescription,
11
+ DialogHeader,
12
+ DialogTitle,
13
+ DialogTrigger,
14
+ } from '../../primitives'
15
+
16
+ import ContactForm from './contact-form'
17
+ import Disclaimer from './disclaimer'
18
+
19
+ const ContactDialog: React.FC<ButtonModalProps> = ({
20
+ open,
21
+ onOpenChange,
22
+ buttonText,
23
+ buttonProps,
24
+ title,
25
+ byline,
26
+ action,
27
+ actionEnclosure
28
+ }) => (
29
+ <Dialog open={open} onOpenChange={onOpenChange} >
30
+ <DialogTrigger asChild>
31
+ <Button {...buttonProps} >{buttonText}</Button>
32
+ </DialogTrigger>
33
+ <DialogContent className="sm:max-w-[500px] p-0 gap-0 bg-background border">
34
+ <DialogHeader className='py-6 text-foreground'>
35
+ <DialogTitle className='text-4xl font-heading text-center text-inherit'>{title}</DialogTitle>
36
+ {byline && (<DialogDescription className='text-inherit text-xl text-center'>{byline} </DialogDescription>)}
37
+ </DialogHeader>
38
+ <div className='p-8 rounded-e-lg flex flex-col justify-start items-center'>
39
+ <ContactForm onSubmit={action} enclosure={actionEnclosure}/>
40
+ <div className='text-muted-1 text-xs mt-4' >
41
+ <Disclaimer />
42
+ </div>
43
+ </div>
44
+ </DialogContent>
45
+ </Dialog>
46
+ )
47
+
48
+ export default ContactDialog
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+
3
+ const FIRST = 2020
4
+
5
+ const Copyright: React.FC<{
6
+ className?: string
7
+ }> = ({
8
+ className=''
9
+ }) => {
10
+
11
+ const year = new Date().getFullYear()
12
+ const yearString = (year > FIRST) ? `${FIRST} - ${year}` : FIRST.toString()
13
+
14
+ return (
15
+ <div className={className}>
16
+ {`Copyright © ${yearString}`}&nbsp;<br className='sm:hidden'/>Lux Partners Ltd.&nbsp;<br className='md:hidden'/>&nbsp;All rights reserved.
17
+ </div>
18
+ )
19
+ }
20
+
21
+ export default Copyright
@@ -0,0 +1,54 @@
1
+ 'use client'
2
+ import React, { type PropsWithChildren, type ReactNode } from 'react'
3
+
4
+ import { X as LucideX} from 'lucide-react'
5
+
6
+ import Logo from './logo'
7
+ import { Sheet, SheetContent, SheetTrigger } from '../primitives/sheet'
8
+
9
+ const DrawerMenu: React.FC<PropsWithChildren & {
10
+ trigger: ReactNode
11
+ className?: string
12
+ showLogo?: boolean,
13
+ propogate?: boolean
14
+ asChild?: boolean
15
+ }> = ({
16
+ trigger,
17
+ children,
18
+ className='',
19
+ showLogo=true,
20
+ propogate=true,
21
+ asChild=false
22
+ }) => {
23
+
24
+ const [open, setOpen] = React.useState(false)
25
+
26
+ const onAction = () => { setOpen(false) }
27
+
28
+ // https://stackoverflow.com/a/49052730/11645689
29
+ const updatedChildren = React.Children.map(
30
+ children,
31
+ (child) => (React.cloneElement(
32
+ child as any, { onAction }
33
+ ))
34
+ )
35
+
36
+ return (
37
+ <Sheet open={open} onOpenChange={setOpen} >
38
+ <SheetTrigger className={className} asChild={asChild}>
39
+ {trigger}
40
+ </SheetTrigger>
41
+ <SheetContent
42
+ side="right"
43
+ closeButtonClass='text-inherit opacity-90'
44
+ onClick={propogate ? onAction : () => {}}
45
+ closeElement={<LucideX className='h-6 w-6 text-inherit'/>}
46
+ centerElement={showLogo ? <Logo size='xs' className='' /> : null}
47
+ >
48
+ {updatedChildren}
49
+ </SheetContent>
50
+ </Sheet>
51
+ )
52
+ }
53
+
54
+ export default DrawerMenu
@@ -0,0 +1,77 @@
1
+ import React from 'react'
2
+
3
+ import type { LinkDef, SiteDef } from '../types'
4
+ import { Copyright } from '.'
5
+ import { NavItems } from '../primitives'
6
+ import { legal } from '../siteDef/footer/legal'
7
+
8
+ import Logo from './logo'
9
+ import { cn } from '../util'
10
+
11
+ const Footer: React.FC<{
12
+ siteDef: SiteDef,
13
+ className?: string,
14
+ noHorizPadding?: boolean
15
+ }> = ({
16
+ siteDef,
17
+ className='',
18
+ noHorizPadding=false
19
+ }) => {
20
+
21
+ const { footer, aboveCopyright } = siteDef
22
+ const smGridCols = Math.floor(footer.length/2)
23
+ const smGridColsClx = `sm:grid-cols-${smGridCols} `
24
+ const _aboveCopyright = (typeof aboveCopyright === 'undefined') ? legal : aboveCopyright
25
+
26
+ return (
27
+ <footer className={cn('grow flex flex-col justify-end gap-6 pb-[2vh]', className)}>
28
+ <div className={
29
+ (noHorizPadding ? '' : 'px-5 md:px-8 ') +
30
+ 'grid grid-cols-2 gap-4 gap-y-6 md:gap-x-6 lg:gap-8 ' + smGridColsClx +
31
+ 'md:w-full sm:justify-items-center md:mx-0 lg:w-full max-w-screen-2xl ' +
32
+ 'md:flex md:flex-row md:justify-between '
33
+ }>
34
+ <div className='hidden lg:flex flex-col' key={0}>
35
+ <Logo size='md' />
36
+ </div>
37
+ {footer.map((defs: LinkDef[], index) => {
38
+
39
+ const xsColSpanClx = ((index === footer.length - 1) && (footer.length % 2 === 1)) ?
40
+ 'xs:col-span-2 xs:mx-auto md:col-span-1 md:mx-0 ' : ''
41
+
42
+ return (
43
+ <NavItems
44
+ items={defs}
45
+ currentAs={siteDef.currentAs}
46
+ as='nav'
47
+ className={cn('sm:min-w-[150px] md:min-w-0 flex flex-col justify-start items-start ' +
48
+ 'gap-[11px] sm:gap-[12px] md:gap-[15px] ',
49
+ xsColSpanClx
50
+ )}
51
+ key={index + 1}
52
+ itemClx={(def: LinkDef) => ((def.variant === 'linkFG') ?
53
+ 'font-nav text-[15px]/[1.3] font-medium text-foreground tracking-normal'
54
+ :
55
+ 'text-[15px]/[1.1] font-normal tracking-[0.2px] text-muted-1'
56
+ )}
57
+ />
58
+ )
59
+ })}
60
+ </div>
61
+ <div className='md:mt-[2vh]'>
62
+ {_aboveCopyright.length > 0 && (
63
+ <NavItems
64
+ items={_aboveCopyright}
65
+ as='div'
66
+ className={'flex flex-row justify-center gap-4 mb-2'}
67
+ itemClx={'text-sm text-center text-muted-2 underline hover:text-foreground'}
68
+ />
69
+ )}
70
+ <Copyright className='text-sm text-center text-muted-3'/>
71
+ </div>
72
+ </footer>
73
+ )
74
+ }
75
+
76
+ export default Footer
77
+ // flex flex-col justify-between gap-6
@@ -0,0 +1,158 @@
1
+ /**
2
+ *
3
+ * Metadata types
4
+ *
5
+ */
6
+
7
+ export interface DeprecatedMetadataFields {
8
+ /**
9
+ * Deprecated options that have a preferred method
10
+ * @deprecated Use appWebApp to configure apple-mobile-web-app-capable which provides
11
+ * @see https://www.appsloveworld.com/coding/iphone/11/difference-between-apple-mobile-web-app-capable-and-apple-touch-fullscreen-ipho
12
+ */
13
+ 'apple-touch-fullscreen'?: never
14
+
15
+ /**
16
+ * Obsolete since iOS 7.
17
+ * @see https://web.dev/apple-touch-icon/
18
+ * @deprecated use icons.apple or instead
19
+ */
20
+ 'apple-touch-icon-precomposed'?: never
21
+ }
22
+
23
+ export type TemplateString =
24
+ | DefaultTemplateString
25
+ | AbsoluteTemplateString
26
+ | AbsoluteString
27
+ export type DefaultTemplateString = {
28
+ default: string
29
+ template: string
30
+ }
31
+ export type AbsoluteTemplateString = {
32
+ absolute: string
33
+ template: string | null
34
+ }
35
+ export type AbsoluteString = {
36
+ absolute: string
37
+ }
38
+
39
+ export type Author = {
40
+ // renders as <link rel="author"...
41
+ url?: string | URL
42
+ // renders as <meta name="author"...
43
+ name?: string
44
+ }
45
+
46
+ // does not include "unsafe-URL". to use this users should
47
+ // use '"unsafe-URL" as ReferrerEnum'
48
+ export type ReferrerEnum =
49
+ | 'no-referrer'
50
+ | 'origin'
51
+ | 'no-referrer-when-downgrade'
52
+ | 'origin-when-cross-origin'
53
+ | 'same-origin'
54
+ | 'strict-origin'
55
+ | 'strict-origin-when-cross-origin'
56
+
57
+ export type ColorSchemeEnum =
58
+ | 'normal'
59
+ | 'light'
60
+ | 'dark'
61
+ | 'light dark'
62
+ | 'dark light'
63
+ | 'only light'
64
+
65
+ type RobotsInfo = {
66
+ // all and none will be inferred from index/follow boolean options
67
+ index?: boolean
68
+ follow?: boolean
69
+
70
+ /** @deprecated set index to false instead */
71
+ noindex?: never
72
+ /** @deprecated set follow to false instead */
73
+ nofollow?: never
74
+
75
+ noarchive?: boolean
76
+ nosnippet?: boolean
77
+ noimageindex?: boolean
78
+ nocache?: boolean
79
+ notranslate?: boolean
80
+ indexifembedded?: boolean
81
+ nositelinkssearchbox?: boolean
82
+ unavailable_after?: string
83
+ 'max-video-preview'?: number | string
84
+ 'max-image-preview'?: 'none' | 'standard' | 'large'
85
+ 'max-snippet'?: number
86
+ }
87
+ export type Robots = RobotsInfo & {
88
+ // if you want to specify an alternate robots just for google
89
+ googleBot?: string | RobotsInfo
90
+ }
91
+
92
+ export type ResolvedRobots = {
93
+ basic: string | null
94
+ googleBot: string | null
95
+ }
96
+
97
+ export type IconURL = string | URL
98
+ export type Icon = IconURL | IconDescriptor
99
+ export type IconDescriptor = {
100
+ url: string | URL
101
+ type?: string
102
+ sizes?: string
103
+ color?: string
104
+ /** defaults to rel="icon" unless superseded by Icons map */
105
+ rel?: string
106
+ media?: string
107
+ /**
108
+ * @see https://developer.mozilla.org/docs/Web/API/HTMLImageElement/fetchPriority
109
+ */
110
+ fetchPriority?: 'high' | 'low' | 'auto'
111
+ }
112
+
113
+ export type Icons = {
114
+ /** rel="icon" */
115
+ icon?: Icon | Icon[]
116
+ /** rel="shortcut icon" */
117
+ shortcut?: Icon | Icon[]
118
+ /**
119
+ * @see https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
120
+ * rel="apple-touch-icon"
121
+ */
122
+ apple?: Icon | Icon[]
123
+ /** rel inferred from descriptor, defaults to "icon" */
124
+ other?: IconDescriptor | IconDescriptor[]
125
+ }
126
+
127
+ export type Verification = {
128
+ google?: null | string | number | (string | number)[]
129
+ yahoo?: null | string | number | (string | number)[]
130
+ yandex?: null | string | number | (string | number)[]
131
+ me?: null | string | number | (string | number)[]
132
+ // if you ad-hoc additional verification
133
+ other?: {
134
+ [name: string]: string | number | (string | number)[]
135
+ }
136
+ }
137
+
138
+ export type ResolvedVerification = {
139
+ google?: null | (string | number)[]
140
+ yahoo?: null | (string | number)[]
141
+ yandex?: null | (string | number)[]
142
+ me?: null | (string | number)[]
143
+ other?: {
144
+ [name: string]: (string | number)[]
145
+ }
146
+ }
147
+
148
+ export type ResolvedIcons = {
149
+ icon: IconDescriptor[]
150
+ apple: IconDescriptor[]
151
+ shortcut?: IconDescriptor[]
152
+ other?: IconDescriptor[]
153
+ }
154
+
155
+ export type ThemeColorDescriptor = {
156
+ color: string
157
+ media?: string
158
+ }