@visualizevalue/mint-app-base 0.0.1

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 (129) hide show
  1. package/.env.example +26 -0
  2. package/README.md +24 -0
  3. package/app/app.vue +7 -0
  4. package/app/assets/styles/animation.css +50 -0
  5. package/app/assets/styles/base.css +34 -0
  6. package/app/assets/styles/cards.css +20 -0
  7. package/app/assets/styles/custom-media.css +4 -0
  8. package/app/assets/styles/custom-selectors.css +1 -0
  9. package/app/assets/styles/forms.css +183 -0
  10. package/app/assets/styles/index.css +11 -0
  11. package/app/assets/styles/normalize.css +541 -0
  12. package/app/assets/styles/prose.css +166 -0
  13. package/app/assets/styles/scroll.css +13 -0
  14. package/app/assets/styles/text.css +14 -0
  15. package/app/assets/styles/utils.css +24 -0
  16. package/app/assets/styles/variables.css +195 -0
  17. package/app/assets/styles/web3-modals.css +26 -0
  18. package/app/components/Account.client.vue +20 -0
  19. package/app/components/Actions.vue +25 -0
  20. package/app/components/AppHeader.vue +99 -0
  21. package/app/components/Authenticated.client.vue +17 -0
  22. package/app/components/Avatar.vue +61 -0
  23. package/app/components/BlocksTimeAgo.client.vue +20 -0
  24. package/app/components/Breadcrumbs.vue +51 -0
  25. package/app/components/Button.vue +98 -0
  26. package/app/components/CardLink.vue +38 -0
  27. package/app/components/CheckSpinner.vue +39 -0
  28. package/app/components/Collection/Intro.vue +111 -0
  29. package/app/components/Collection/OverviewCard.vue +73 -0
  30. package/app/components/Collection/Withdraw.client.vue +61 -0
  31. package/app/components/CollectionsOverview.client.vue +58 -0
  32. package/app/components/Connect.client.vue +88 -0
  33. package/app/components/CountDown.vue +153 -0
  34. package/app/components/DialogFrame.vue +96 -0
  35. package/app/components/ExpandableText.vue +50 -0
  36. package/app/components/Form/Errors.vue +18 -0
  37. package/app/components/Form/Group.vue +57 -0
  38. package/app/components/Form/Input.vue +48 -0
  39. package/app/components/Form/SelectFile.vue +60 -0
  40. package/app/components/GasPrice.client.vue +9 -0
  41. package/app/components/HeaderSection.vue +18 -0
  42. package/app/components/Icon.vue +37 -0
  43. package/app/components/IconLink.vue +29 -0
  44. package/app/components/Image.client.vue +120 -0
  45. package/app/components/Loading.vue +79 -0
  46. package/app/components/MintGasPrice.client.vue +20 -0
  47. package/app/components/MintGasPricePopover.client.vue +69 -0
  48. package/app/components/MintToken.vue +89 -0
  49. package/app/components/MintTokenBar.vue +79 -0
  50. package/app/components/Modal.vue +36 -0
  51. package/app/components/Navbar.client.vue +86 -0
  52. package/app/components/Page/Frame.vue +77 -0
  53. package/app/components/Page/FrameSM.vue +33 -0
  54. package/app/components/Popover.client.vue +119 -0
  55. package/app/components/Profile/Header.client.vue +96 -0
  56. package/app/components/QueryDialog.vue +38 -0
  57. package/app/components/ToggleDarkMode.client.vue +58 -0
  58. package/app/components/Token/Detail.client.vue +194 -0
  59. package/app/components/Token/MintTimeline.client.vue +110 -0
  60. package/app/components/Token/MintTimelineItem.vue +33 -0
  61. package/app/components/Token/OverviewCard.vue +140 -0
  62. package/app/components/TransactionFlow.vue +225 -0
  63. package/app/components/Visual/ImagePreview.vue +8 -0
  64. package/app/composables/account.ts +21 -0
  65. package/app/composables/app.ts +15 -0
  66. package/app/composables/artistData.ts +22 -0
  67. package/app/composables/chainId.ts +25 -0
  68. package/app/composables/collections.ts +435 -0
  69. package/app/composables/darkMode.ts +1 -0
  70. package/app/composables/gasPrice.ts +46 -0
  71. package/app/composables/head.ts +29 -0
  72. package/app/composables/priceFeed.ts +80 -0
  73. package/app/composables/subdomain.ts +27 -0
  74. package/app/error.vue +31 -0
  75. package/app/layouts/default.vue +42 -0
  76. package/app/middleware/lowercaseId.ts +1 -0
  77. package/app/middleware/lowercaseProfileAddress.ts +1 -0
  78. package/app/middleware/redirectUserScope.ts +13 -0
  79. package/app/pages/[id]/[collection]/[tokenId]/index.vue +66 -0
  80. package/app/pages/[id]/[collection]/[tokenId].vue +25 -0
  81. package/app/pages/[id]/[collection]/index.vue +51 -0
  82. package/app/pages/[id]/[collection]/mint.vue +260 -0
  83. package/app/pages/[id]/[collection].vue +24 -0
  84. package/app/pages/[id]/add.vue +40 -0
  85. package/app/pages/[id]/create.vue +177 -0
  86. package/app/pages/[id]/index.vue +43 -0
  87. package/app/pages/[id].vue +9 -0
  88. package/app/pages/index.vue +47 -0
  89. package/app/pages/profile/[address]/index.vue +51 -0
  90. package/app/pages/profile/[address].vue +9 -0
  91. package/app/pages/profile/index.vue +28 -0
  92. package/app/plugins/1.polyfill.client.ts +12 -0
  93. package/app/plugins/2.wagmi.ts +57 -0
  94. package/app/router.options.ts +25 -0
  95. package/app/utils/abis.ts +77 -0
  96. package/app/utils/arrays.ts +1 -0
  97. package/app/utils/artifact.ts +21 -0
  98. package/app/utils/breakpoints.ts +11 -0
  99. package/app/utils/dates.ts +23 -0
  100. package/app/utils/format.ts +60 -0
  101. package/app/utils/images.ts +27 -0
  102. package/app/utils/ipfs.ts +13 -0
  103. package/app/utils/lowercaseRouteParam.ts +10 -0
  104. package/app/utils/serializer.ts +18 -0
  105. package/app/utils/strings.ts +30 -0
  106. package/app/utils/time.ts +23 -0
  107. package/app/utils/types.ts +62 -0
  108. package/app/utils/urls.ts +43 -0
  109. package/nuxt.config.ts +130 -0
  110. package/package.json +44 -0
  111. package/public/apple-touch-icon-512x512.png +0 -0
  112. package/public/example-contract-icon-original.svg +5 -0
  113. package/public/example-contract-icon.svg +5 -0
  114. package/public/favicon.ico +0 -0
  115. package/public/icon.svg +8 -0
  116. package/public/icons/check.svg +3 -0
  117. package/public/icons/opepen.svg +264 -0
  118. package/public/icons/wallets/coinbase.svg +4 -0
  119. package/public/icons/wallets/metamask.svg +1 -0
  120. package/public/icons/wallets/rainbow.svg +59 -0
  121. package/public/icons/wallets/walletconnect.svg +1 -0
  122. package/public/maskable-icon-512x512.png +0 -0
  123. package/public/pwa-192x192.png +0 -0
  124. package/public/pwa-512x512.png +0 -0
  125. package/public/pwa-64x64.png +0 -0
  126. package/server/middleware/log.ts +3 -0
  127. package/server/middleware/subdomain.ts +12 -0
  128. package/server/tsconfig.json +3 -0
  129. package/tsconfig.json +4 -0
@@ -0,0 +1,27 @@
1
+ export type ImageVersions = {
2
+ xs?: boolean, // 215
3
+ sm?: boolean, // 512
4
+ md?: boolean, // 1024
5
+ lg?: boolean, // 2048
6
+ }
7
+
8
+ export type Image = {
9
+ id: string,
10
+ versions: ImageVersions,
11
+ cdn: string,
12
+ path: string,
13
+ type: string,
14
+ aspectRatio?: number,
15
+ }
16
+
17
+ const CDN_BASE = `cdn.evverydays.com`
18
+
19
+ export const imageURI = (image: Image, version?: keyof ImageVersions) => {
20
+ if (! image) return null
21
+
22
+ const name = (version && image.versions[version])
23
+ ? `${image.id}@${version}.webp`
24
+ : `${image.id}.${image.type}`
25
+
26
+ return `https://${image.cdn}.${CDN_BASE}/${image.path}/${name}`
27
+ }
@@ -0,0 +1,13 @@
1
+ import { CID } from 'multiformats/cid'
2
+
3
+ export const validateCID = (cid: string): string|false => {
4
+ try {
5
+ const parsed = CID.parse(cid)
6
+
7
+ return parsed.toString()
8
+ } catch (e) {
9
+ console.warn(`Invalid CID`)
10
+
11
+ return false
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ import type { RouteLocationNormalizedGeneric } from 'vue-router'
2
+
3
+ export const lowercaseRouteParam = (to: RouteLocationNormalizedGeneric, param: string) => {
4
+ if (to.params[param] && typeof to.params[param] === 'string') {
5
+ const before = `${to.params[param]}`
6
+ to.params[param] = to.params[param].toLowerCase()
7
+
8
+ if (before !== to.params[param]) return navigateTo(to, { redirectCode: 301 })
9
+ }
10
+ }
@@ -0,0 +1,18 @@
1
+ // Custom replacer function for JSON.stringify
2
+ const replacer = (_: string, value: any) => {
3
+ if (typeof value === 'bigint') {
4
+ return value.toString() + 'n'
5
+ }
6
+ return value
7
+ }
8
+
9
+ // Custom reviver function for JSON.parse
10
+ const reviver = (_: string, value: any) => {
11
+ if (typeof value === 'string' && /^\d+n$/.test(value)) {
12
+ return BigInt(value.slice(0, -1))
13
+ }
14
+ return value
15
+ }
16
+
17
+ export const stringifyJSON = (obj: any): string => JSON.stringify(obj, replacer)
18
+ export const parseJSON = (json: string): any => JSON.parse(json, reviver)
@@ -0,0 +1,30 @@
1
+ export const pluralize = (word: string, count: number): string => {
2
+ if (count === 1) return word
3
+
4
+ // Basic rules for pluralization
5
+ if (word.endsWith('y')) {
6
+ return word.slice(0, -1) + 'ies'
7
+ } else if (word.endsWith('s') || word.endsWith('ch') || word.endsWith('sh') || word.endsWith('x')) {
8
+ return word + 'es'
9
+ } else {
10
+ return word + 's'
11
+ }
12
+ }
13
+
14
+ export const cleanText = (str: string) => str?.replace(/<[^>]*>?/gm, '').trim() || ''
15
+
16
+ export const shortenedCleanText = (str: string, length: number = 80) => {
17
+ const txt = cleanText(str)
18
+
19
+ const nextSpaceIndex = txt.indexOf(' ', length)
20
+
21
+ return txt.length > length && nextSpaceIndex > 0 ? txt.substring(0, nextSpaceIndex) + '...' : txt
22
+ }
23
+
24
+ const urlPattern = /\b((http|https):\/\/)?(www\.)?([a-zA-Z0-9\-\.]+)\.([a-zA-Z]{2,})(\/[^\s]*)?\b/g
25
+ export const extractURLs = (str: string) => {
26
+ return {
27
+ text: str.replace(urlPattern, ''),
28
+ urls: str.match(urlPattern),
29
+ }
30
+ }
@@ -0,0 +1,23 @@
1
+ export const BLOCKS_PER_CACHE = 5n * 30n // 30 minutes (5 blocks each)
2
+ export const BLOCKS_PER_HOUR = 300n // 5n * 60n * 24n (5 blocks/min * 60 min * 24 hours)
3
+ export const BLOCKS_PER_DAY = 7200n // 300n * 24n (300 blocks per hour * 24 hours)
4
+
5
+ export const delay = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
6
+
7
+ export const blocksToSeconds = (blocks: bigint): number => Number(blocks * 12n)
8
+
9
+ export const nowInSeconds = (): number => Math.floor(Date.now() / 1000)
10
+
11
+ const now = ref(nowInSeconds())
12
+ let nowInterval: NodeJS.Timeout
13
+ export const useNow = () => {
14
+ onMounted(() => {
15
+ if (! nowInterval) {
16
+ nowInterval = setInterval(() => {
17
+ now.value = nowInSeconds()
18
+ }, 1000)
19
+ }
20
+ })
21
+
22
+ return now
23
+ }
@@ -0,0 +1,62 @@
1
+ import type { RouteLocationRaw } from 'vue-router'
2
+
3
+ // =====================================================================
4
+ // APP-LOGIC
5
+ // =====================================================================
6
+ export type BreadcrumbItem = string|{
7
+ to: RouteLocationRaw
8
+ text: string
9
+ }
10
+ export type Breadcrumbs = BreadcrumbItem[]
11
+
12
+ // =====================================================================
13
+ // DATA
14
+ // =====================================================================
15
+ export interface Artist {
16
+ address: `0x${string}`
17
+ ens?: string|null
18
+ avatar?: string|null
19
+ description?: string|null
20
+ url?: string|null
21
+ email?: string|null
22
+ twitter?: string|null
23
+ github?: string|null
24
+ collections: `0x${string}`[]
25
+ profileUpdatedAtBlock: bigint
26
+ }
27
+
28
+ export interface Collection {
29
+ address: `0x${string}`
30
+ owner: `0x${string}`
31
+ image: string
32
+ name: string
33
+ symbol: string
34
+ description: string
35
+ initBlock: bigint
36
+ latestTokenId: bigint
37
+ tokens: { [key: string]: Token }
38
+ balance: bigint
39
+ }
40
+
41
+ export interface Token {
42
+ collection: `0x${string}`
43
+ tokenId: bigint
44
+ name: string
45
+ description: string
46
+ artifact: string
47
+ untilBlock: bigint
48
+ mintsFetchedUntilBlock: bigint
49
+ mintsBackfilledUntilBlock: bigint
50
+ mints: MintEvent[]
51
+ }
52
+
53
+ export interface MintEvent {
54
+ tokenId: bigint
55
+ address: `0x${string}`
56
+ block: bigint
57
+ logIndex: number
58
+ tx: string
59
+ unitPrice: bigint
60
+ amount: bigint
61
+ price: bigint
62
+ }
@@ -0,0 +1,43 @@
1
+ export const ipfsToHttpURI = (url: string, gateway: string = 'https://ipfs.io/ipfs/') => url.replace('ipfs://', gateway)
2
+
3
+ export const validateURI = (url: string) => {
4
+ if (! url || ! url.length) return false
5
+
6
+ let validated = url.trim()
7
+
8
+ // Normalize protocol
9
+ if (! validated.startsWith('https://')) validated = `https://${validated}`
10
+ if (! validated.startsWith('http')) validated = `http://${validated}`
11
+
12
+ // Check url validity
13
+ try {
14
+ new URL(validated)
15
+ } catch (e) {
16
+ return false
17
+ }
18
+
19
+ return validated
20
+ }
21
+
22
+ export const getMainDomain = (url: string) => {
23
+ const regex = /^(?:https?:\/\/)?(?:www\.)?([^\/\n]+)/i
24
+ const match = url.match(regex)
25
+ return match ? match[1] : null
26
+ }
27
+
28
+ export const getFirstSubpath = (url: string) => {
29
+ try {
30
+ // This will throw an error if the URL is not valid
31
+ const parsedUrl = new URL(url)
32
+ const pathname = parsedUrl.pathname
33
+
34
+ // Remove the leading slash and split by slash to get path segments
35
+ const pathSegments = pathname.replace(/^\//, '').split('/')
36
+
37
+ // Return the first path segment, if there is one
38
+ return pathSegments.length > 0 ? pathSegments[0] : null
39
+ } catch (e) {
40
+ // The URL is invalid
41
+ return null
42
+ }
43
+ }
package/nuxt.config.ts ADDED
@@ -0,0 +1,130 @@
1
+ import { fileURLToPath } from 'url'
2
+ import { dirname, join } from 'path'
3
+
4
+ const currentDir = dirname(fileURLToPath(import.meta.url))
5
+
6
+ // https://nuxt.com/docs/api/configuration/nuxt-config
7
+ export default defineNuxtConfig({
8
+ devtools: { enabled: true },
9
+ ssr: process.env.NUXT_SSR !== 'false',
10
+
11
+ runtimeConfig: {
12
+ public: {
13
+ title: 'Mint',
14
+ description: 'To mint is a human right.',
15
+ blockExplorer: 'https://etherscan.io',
16
+ creatorAddress: '',
17
+ factoryAddress: '',
18
+ chainId: 1337,
19
+ walletConnectProjectId: '',
20
+ platformUrl: 'https://networked.art',
21
+ }
22
+ },
23
+
24
+ app: {
25
+ head: {
26
+ viewport: 'width=device-width, initial-scale=1, viewport-fit=cover',
27
+ htmlAttrs: { lang: 'en' },
28
+ title: process.env.NUXT_PUBLIC_TITLE,
29
+ meta: [
30
+ { name: 'twitter:card', content: 'summary_large_image' },
31
+ { name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
32
+ { name: 'theme-color', content: 'black' },
33
+ { name: 'theme-color', media: '(prefers-color-scheme: light)', content: 'white' },
34
+ { name: 'theme-color', media: '(prefers-color-scheme: dark)', content: 'black' },
35
+ ],
36
+ link: [
37
+ { rel: 'icon', href: '/icon.svg', type: 'image/svg+xml' },
38
+ ]
39
+ }
40
+ },
41
+
42
+ css: [
43
+ join(currentDir, './app/assets/styles/index.css'),
44
+ ],
45
+
46
+ postcss: {
47
+ plugins: {
48
+ '@csstools/postcss-global-data': {
49
+ files: [
50
+ join(currentDir, './app/assets/styles/custom-selectors.css'),
51
+ join(currentDir, './app/assets/styles/custom-media.css'),
52
+ ]
53
+ },
54
+ 'postcss-nested': {},
55
+ 'postcss-custom-selectors': {},
56
+ 'postcss-custom-media': {},
57
+ 'postcss-preset-env': {
58
+ stage: 3,
59
+ features: {},
60
+ },
61
+ 'autoprefixer': {},
62
+ },
63
+ },
64
+
65
+ modules: [
66
+ '@pinia/nuxt',
67
+ '@pinia-plugin-persistedstate/nuxt',
68
+ '@vueuse/nuxt',
69
+ ],
70
+
71
+ devServer: {
72
+ port: 1618,
73
+ host: process.env.NUXT_PUBLIC_DOMAIN || 'localhost',
74
+ },
75
+
76
+ vite: {
77
+ optimizeDeps: {
78
+ force: true,
79
+ include: [
80
+ '@wagmi/core > eventemitter3'
81
+ ],
82
+ },
83
+ },
84
+
85
+ nitro: {
86
+ preset: 'node-cluster',
87
+ esbuild: {
88
+ options: {
89
+ target: 'esnext'
90
+ }
91
+ },
92
+ },
93
+
94
+ imports: {
95
+ presets: [
96
+ {
97
+ from: '@wagmi/core',
98
+ imports: [
99
+ 'readContract',
100
+ 'waitForTransactionReceipt',
101
+ 'writeContract',
102
+ ]
103
+ },
104
+ {
105
+ from: 'viem',
106
+ imports: [
107
+ 'decodeEventLog',
108
+ 'isAddress',
109
+ 'getAddress',
110
+ 'toBytes',
111
+ 'toHex',
112
+ 'getContract',
113
+ 'encodeAbiParameters',
114
+ 'parseAbiParameters',
115
+ 'parseAbiParameter',
116
+ ]
117
+ }
118
+ ]
119
+ },
120
+
121
+ piniaPersistedstate: {
122
+ storage: 'localStorage'
123
+ },
124
+
125
+ future: {
126
+ compatibilityVersion: 4,
127
+ },
128
+
129
+ compatibilityDate: '2024-08-14',
130
+ })
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@visualizevalue/mint-app-base",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "./nuxt.config.ts",
6
+ "dependencies": {
7
+ "@csstools/postcss-global-data": "^2.1.1",
8
+ "@pinia-plugin-persistedstate/nuxt": "^1.2.1",
9
+ "@pinia/nuxt": "^0.5.3",
10
+ "@tanstack/vue-query": ">=5.45.0",
11
+ "@visualizevalue/mint-utils": "npm:@visualizevalue/mint-utils@^0.0.1",
12
+ "@vueuse/components": "^10.11.0",
13
+ "@wagmi/vue": "^0.0.40",
14
+ "buffer": "^6.0.3",
15
+ "eventemitter3": "^5.0.1",
16
+ "feather-icons": "^4.29.2",
17
+ "multiformats": "^13.2.2",
18
+ "postcss-custom-media": "^10.0.6",
19
+ "postcss-custom-selectors": "^7.1.10",
20
+ "postcss-nested": "^6.0.1",
21
+ "postcss-preset-env": "^9.5.13",
22
+ "viem": "2.x",
23
+ "vue-feather": "^2.0.0",
24
+ "vue-router": "^4.3.2",
25
+ "vue-virtual-scroller": "^2.0.0-beta.8"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.5.4",
29
+ "@vue/devtools-api": "^6.6.3",
30
+ "@vueuse/core": "^11.0.3",
31
+ "@vueuse/nuxt": "^11.0.3",
32
+ "eventemitter3": "^5.0.1",
33
+ "nuxt": "latest",
34
+ "typescript": "^5.5.4",
35
+ "vite": "^5.4.3"
36
+ },
37
+ "scripts": {
38
+ "build": "nuxt build",
39
+ "dev": "nuxt dev --port 1618",
40
+ "generate": "nuxt generate",
41
+ "preview": "nuxt preview",
42
+ "postinstall": "nuxt prepare"
43
+ }
44
+ }
@@ -0,0 +1,5 @@
1
+ <svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="64" height="64" fill="black"/>
3
+ <path d="M29.3984 34.2759C29.3984 35.7425 28.9263 36.9437 27.9821 37.8793L22.839 43H19.5593L14.4162 37.8793C13.4721 36.9437 13 35.7425 13 34.2759V21H16.8387V34.1241C16.8387 34.7563 17.0002 35.2368 17.3232 35.5655L21.2365 39.5862L25.0752 35.5655C25.3982 35.2368 25.5597 34.7563 25.5597 34.1241V21H29.3984V34.2759Z" fill="white"/>
4
+ <path d="M52 34.2759C52 35.7425 51.5279 36.9437 50.5838 37.8793L45.4407 43H42.161L37.0178 37.8793C36.0737 36.9437 35.6016 35.7425 35.6016 34.2759V21H39.4403V34.1241C39.4403 34.7563 39.6018 35.2368 39.9248 35.5655L43.8381 39.5862L47.6768 35.5655C47.9998 35.2368 48.1613 34.7563 48.1613 34.1241V21H52V34.2759Z" fill="white"/>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="64" height="64" fill="#000"/>
3
+ <path d="m30 36-7 7h-3l-7-7v-15h4v13l4 4h1l4-4v-13h4v15z" fill="#fff"/>
4
+ <path d="m51 36-7 7h-3l-7-7v-15h4v13l4 4h1l4-4v-13h4v15z" fill="#fff"/>
5
+ </svg>
Binary file
@@ -0,0 +1,8 @@
1
+ <svg width="140" height="140" viewBox="0 0 140 140" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="140" height="140" fill="#131313"/>
3
+ <path d="M24.5 38V24.5H115.5V38V115.5H24.5V38Z" stroke="white" stroke-width="5"/>
4
+ <path d="M24.5 38V24.5H115.5V38V115.5H24.5V38Z" stroke="white" stroke-width="5"/>
5
+ <path d="M118 38H22" stroke="white" stroke-width="5"/>
6
+ <path d="M92.9145 61.0682C92.9145 60.4578 93.3723 60 93.9827 60H97.0347C97.6452 60 98.103 60.4578 98.103 61.0682V81.6696C98.103 81.7714 98.103 81.924 98.0521 82.0766L97.4926 83.3991C97.3908 83.5517 97.34 83.6535 97.2382 83.7552L87.9803 93.0131C87.7768 93.2166 87.5225 93.3183 87.2173 93.3183H84.5722C84.3178 93.3183 84.0126 93.2166 83.8092 93.0131L74.5512 83.7552C74.4495 83.6535 74.3986 83.5517 74.3478 83.3991L73.7882 82.0766C73.7374 81.924 73.6865 81.7714 73.6865 81.6696V61.0682C73.6865 60.4578 74.1443 60 74.7547 60H77.8068C78.4172 60 78.875 60.4578 78.875 61.0682V80.2962C78.875 80.6014 79.0276 80.8557 79.1802 81.0592L85.1317 86.9599C85.5387 87.4177 86.2508 87.4177 86.6577 86.9599L92.6093 81.0592C92.8127 80.8557 92.9145 80.6014 92.9145 80.2962V61.0682Z" fill="white"/>
7
+ <path d="M60.228 61.0682C60.228 60.4578 60.6858 60 61.2962 60H64.3483C64.9587 60 65.4165 60.4578 65.4165 61.0682V81.6696C65.4165 81.7714 65.4165 81.924 65.3656 82.0766L64.8061 83.3991C64.7043 83.5517 64.6535 83.6535 64.5517 83.7552L55.2938 93.0131C55.0903 93.2166 54.836 93.3183 54.5308 93.3183H51.8857C51.6313 93.3183 51.3261 93.2166 51.1227 93.0131L41.8648 83.7552C41.763 83.6535 41.7121 83.5517 41.6613 83.3991L41.1017 82.0766C41.0509 81.924 41 81.7714 41 81.6696V61.0682C41 60.4578 41.4578 60 42.0682 60H45.1203C45.7307 60 46.1885 60.4578 46.1885 61.0682V80.2962C46.1885 80.6014 46.3411 80.8557 46.4937 81.0592L52.4452 86.9599C52.8522 87.4177 53.5643 87.4177 53.9713 86.9599L59.9228 81.0592C60.1262 80.8557 60.228 80.6014 60.228 80.2962V61.0682Z" fill="white"/>
8
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M15.36 3.88585C15.0088 3.30969 14.5152 2.83358 13.9268 2.50331C13.3383 2.17304 12.6748 1.99971 12 2C10.577 2 9.33 2.75494 8.64 3.88685C7.98396 3.72742 7.29792 3.73945 6.64787 3.92179C5.99782 4.10413 5.40562 4.45064 4.92823 4.928C4.45083 5.40536 4.10429 5.99751 3.92194 6.64751C3.73959 7.29751 3.72756 7.98349 3.887 8.63948C3.31061 8.99055 2.83426 9.48402 2.50379 10.0724C2.17331 10.6608 1.99982 11.3244 2 11.9992C2 13.4221 2.755 14.668 3.886 15.359C3.72655 16.0149 3.73859 16.7009 3.92094 17.3509C4.10329 18.0009 4.44983 18.5931 4.92723 19.0704C5.40462 19.5478 5.99682 19.8943 6.64687 20.0766C7.29692 20.259 7.98296 20.271 8.639 20.1116C9.1267 20.913 9.88486 21.5137 10.7766 21.8053C11.6684 22.0968 12.635 22.06 13.502 21.7015C14.2737 21.3815 14.9241 20.8252 15.36 20.1126C16.0161 20.2721 16.7023 20.2601 17.3525 20.0777C18.0026 19.8953 18.5949 19.5487 19.0723 19.0712C19.5498 18.5937 19.8963 18.0014 20.0785 17.3513C20.2608 16.7011 20.2727 16.015 20.113 15.359C20.6894 15.0079 21.1657 14.5144 21.4962 13.926C21.8267 13.3376 22.0002 12.6741 22 11.9992C22.0002 11.3244 21.8267 10.6608 21.4962 10.0724C21.1657 9.48402 20.6894 8.99055 20.113 8.63948C20.2723 7.98354 20.2601 7.29765 20.0776 6.64778C19.8951 5.99791 19.5485 5.40592 19.071 4.92877C18.594 4.45131 18.0022 4.10459 17.3525 3.92194C16.7027 3.7393 16.0169 3.72687 15.361 3.88585H15.36ZM11.402 15.5979L15.964 8.75447C16.53 7.90854 15.213 7.03061 14.648 7.87654L10.622 13.9191L9.251 12.5512C8.534 11.8292 7.415 12.9471 8.135 13.6671L10.305 15.8169C10.3913 15.8749 10.4883 15.9152 10.5903 15.9356C10.6923 15.9559 10.7973 15.9558 10.8993 15.9354C11.0013 15.9149 11.0982 15.8745 11.1845 15.8164C11.2708 15.7583 11.3447 15.6837 11.402 15.5969V15.5979Z" fill="white"></path>
3
+ </svg>