@visualizevalue/mint-app-base 0.1.48 → 0.1.50

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/app.config.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export default defineAppConfig({
2
2
  knownRenderers: [
3
3
  {
4
+ component: 'P5',
4
5
  name: 'P5 Renderer',
5
6
  version: 1n,
6
- address: '0x6a08e806c7ad85b4f4720e76fdd8219a364b710e',
7
+ address: '0xf6f1f8ea7bbc82a3b3da6fba1c24408e7a9a8fab',
7
8
  description: 'Allows using P5 scripts as the artifact content'
8
9
  },
9
10
  ],
10
11
  })
12
+
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <div class="code-editor">
3
+ <CodeMirror
4
+ :value="modelValue"
5
+ :options="cmOptions"
6
+ height="100%"
7
+ :placeholder="placeholder"
8
+ @change="$emit('update:modelValue', $event)"
9
+ original-style
10
+ />
11
+ </div>
12
+ </template>
13
+
14
+ <script setup>
15
+ import CodeMirror from 'codemirror-editor-vue3'
16
+ import 'codemirror/addon/display/placeholder.js'
17
+ import 'codemirror/mode/htmlmixed/htmlmixed.js'
18
+ import 'codemirror/mode/javascript/javascript.js'
19
+ // import 'codemirror/theme/ayu-mirage.css'
20
+
21
+ const props = defineProps({
22
+ modelValue: String,
23
+ placeholder: String,
24
+ mode: {
25
+ type: String,
26
+ default: 'text/javascript'
27
+ },
28
+ })
29
+ const emit = defineEmits(['update:modelValue'])
30
+
31
+ const cmOptions = computed(() => ({
32
+ mode: props.mode,
33
+ // theme: 'ayu-mirage',
34
+ indentUnit: 2,
35
+ tabSize: 2,
36
+ indentWithTab: false
37
+ }))
38
+ </script>
39
+
40
+ <style scoped>
41
+ .code-editor {
42
+ text-transform: none;
43
+ height: 100%;
44
+
45
+ :deep(.CodeMirror) {
46
+ height: 100%;
47
+ }
48
+ }
49
+ </style>
@@ -8,13 +8,6 @@
8
8
  <Icon type="add" />
9
9
  <span>Mint</span>
10
10
  </Button>
11
- <Button
12
- :to="{ name: 'id-collection-renderers', params: { id, collection: collection.address } }"
13
- id="renderers"
14
- >
15
- <Icon type="code" />
16
- <span>Renderers</span>
17
- </Button>
18
11
  </menu>
19
12
  </template>
20
13
 
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <div class="embed">
3
+ <iframe
4
+ ref="frame"
5
+ frameborder="0"
6
+ :src="src"
7
+ sandbox="allow-scripts"
8
+ ></iframe>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup>
13
+ defineProps({
14
+ src: String,
15
+ })
16
+ </script>
17
+
18
+ <style scoped>
19
+ .embed {
20
+ width: 100%;
21
+ height: 0;
22
+ padding-bottom: 100%;
23
+ position: relative;
24
+
25
+ iframe {
26
+ width: 100%;
27
+ height: 100%;
28
+ position: absolute;
29
+ border: var(--border);
30
+ }
31
+ }
32
+ </style>
33
+
@@ -31,6 +31,10 @@ const { files, open, reset, onChange } = useFileDialog({
31
31
 
32
32
  const file = computed(() => files.value?.length ? files.value[0] : null)
33
33
  onChange(() => emit('change', file.value))
34
+
35
+ defineExpose({
36
+ reset,
37
+ })
34
38
  </script>
35
39
 
36
40
  <style scoped>
@@ -1,29 +1,44 @@
1
+ <script setup>
2
+ import Base from './Renderer/Base.client.vue'
3
+ import P5 from './Renderer/P5.client.vue'
4
+
5
+ const components = {
6
+ Base,
7
+ P5,
8
+ }
9
+
10
+ const props = defineProps(['collection'])
11
+ const { component } = useCreateMintRendererComponent(props.collection)
12
+ </script>
13
+
1
14
  <template>
2
15
  <div class="mint-detail">
16
+ <MintSelectRenderer :collection="collection" class="borderless" />
17
+
3
18
  <MintPreview />
4
19
 
5
- <MintRendererBase class="card" />
20
+ <component :is="components[component]" class="card" />
6
21
 
7
22
  <MintAction :collection="collection" />
8
23
  </div>
9
24
  </template>
10
25
 
11
- <script setup>
12
- const props = defineProps(['collection'])
13
- </script>
14
-
15
26
  <style>
16
27
  .mint-detail {
17
28
  display: grid;
18
29
  gap: var(--spacer);
19
30
 
20
- > * {
31
+ > *:not(.borderless) {
21
32
  border: var(--border);
22
33
  padding: var(--spacer);
23
34
  }
24
35
 
25
36
  @media (--md) {
26
37
  grid-template-columns: 40% 1fr;
38
+
39
+ .mint-select-renderer {
40
+ grid-column: span 2;
41
+ }
27
42
  }
28
43
 
29
44
  @media (--lg) {
@@ -31,3 +46,4 @@ const props = defineProps(['collection'])
31
46
  }
32
47
  }
33
48
  </style>
49
+
@@ -1,7 +1,12 @@
1
1
  <template>
2
2
  <article class="mint-preview">
3
- <Image v-if="image" :src="image" alt="Preview" />
4
- <ImagePreview v-else />
3
+ <div class="static">
4
+ <Image v-if="image" :src="image" alt="Preview" />
5
+ <ImagePreview v-else />
6
+ </div>
7
+
8
+ <Embed v-if="animationUrl" :src="animationUrl" />
9
+
5
10
  <h1 :class="{ '': !name }">{{ name || 'Token' }}</h1>
6
11
  <p :class="{ '': !description }">
7
12
  {{ description || 'No description' }}
@@ -10,7 +15,7 @@
10
15
  </template>
11
16
 
12
17
  <script setup>
13
- const { image, name, description } = useCreateMintData()
18
+ const { image, animationUrl, name, description } = useCreateMintData()
14
19
  </script>
15
20
 
16
21
  <style scoped>
@@ -18,14 +23,24 @@ const { image, name, description } = useCreateMintData()
18
23
  height: 100%;
19
24
  place-content: center;
20
25
 
26
+ svg {
27
+ box-shadow: var(--border-shadow);
28
+ }
29
+
21
30
  .image,
22
31
  svg {
23
32
  margin-bottom: var(--spacer-sm);
24
33
  width: 100%;
25
34
  }
26
35
 
27
- svg {
28
- box-shadow: var(--border-shadow);
36
+ .static {
37
+ &:has(+ .embed) {
38
+ width: 30%;
39
+ }
40
+ }
41
+
42
+ .embed {
43
+ margin: var(--spacer-sm) 0;
29
44
  }
30
45
 
31
46
  h1 {
@@ -12,7 +12,7 @@
12
12
 
13
13
  <div>
14
14
  <div v-if="mode === 'file'">
15
- <FormSelectFile @change="setArtifact" />
15
+ <FormSelectFile ref="select" @change="setArtifact" />
16
16
  <p v-if="! isSmall" class="muted">
17
17
  <small>
18
18
  Note: This should be a small file, prefferably an SVG like <a href="https://presence.art/tokens/perspective.svg" target="_blank">this one (810 bytes)</a>.
@@ -30,6 +30,13 @@
30
30
  </template>
31
31
 
32
32
  <script setup>
33
+ const props = defineProps({
34
+ decoupleArtifact: {
35
+ type: Boolean,
36
+ default: false,
37
+ },
38
+ })
39
+
33
40
  const {
34
41
  artifact,
35
42
  image,
@@ -37,36 +44,44 @@ const {
37
44
  description,
38
45
  } = useCreateMintData()
39
46
 
47
+ const select = ref()
40
48
  const mode = ref('file')
41
49
  const ipfsCid = ref('')
42
50
  const arTxId= ref('')
43
51
 
44
- const artifactSize = ref(0)
45
- const isSmall = computed(() => artifactSize.value / 1024 < 10)
52
+ const imageSize = ref(0)
53
+ const isSmall = computed(() => imageSize.value / 1024 < 10)
46
54
  const setArtifact = async (file) => {
47
55
  try {
48
- artifact.value = await imageFileToDataUri(file)
49
- artifactSize.value = file.size
56
+ image.value = await imageFileToDataUri(file)
57
+ imageSize.value = file.size
50
58
  } catch (e) {
51
- artifact.value = ''
52
- artifactSize.value = 0
59
+ image.value = ''
60
+ imageSize.value = 0
53
61
  }
54
62
  }
55
63
  watch(ipfsCid, () => {
56
64
  const validated = validateCID(ipfsCid.value)
57
65
  if (! validated) {
58
- artifact.value = ''
66
+ image.value = ''
59
67
  } else {
60
- artifact.value = ipfsToHttpURI(`ipfs://${validated}`)
68
+ image.value = ipfsToHttpURI(`ipfs://${validated}`)
61
69
  }
62
70
  })
63
71
  watch(arTxId, () => {
64
- artifact.value = `https://arweave.net/${arTxId.value}`
72
+ image.value = `https://arweave.net/${arTxId.value}`
65
73
  })
66
- watch(mode, () => artifact.value = '')
74
+ watch(mode, () => image.value = '')
75
+
76
+ watch(image, () => {
77
+ if (props.decoupleArtifact) return
67
78
 
68
- // Simple and stupid for the base renderer..
69
- watch(artifact, () => image.value = artifact.value)
79
+ // Copy to image (simple and stupid for the base renderer...)
80
+ artifact.value = image.value
81
+
82
+ // If artifact is empty, reset the select field
83
+ if (! artifact.value) select.value.reset()
84
+ })
70
85
  </script>
71
86
 
72
87
  <style scoped>
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <div class="mint-renderer-p5">
3
+
4
+ <Tabs initial="base">
5
+ <template #menu="{ active, select }">
6
+ <Button @click="select('base')" :class="{ active: active === 'base' }">Static</Button>
7
+ <Button @click="select('script')" :class="{ active: active === 'script' }">P5 Script</Button>
8
+ </template>
9
+ <template #content="{ active }">
10
+ <MintRendererBase v-show="active === 'base'" decouple-artifact />
11
+ <CodeEditor v-show="active === 'script'" v-model="script" class="full" />
12
+ </template>
13
+ </Tabs>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup>
18
+ const {
19
+ artifact,
20
+ image,
21
+ animationUrl,
22
+ } = useCreateMintData()
23
+
24
+ const script = ref(DEFAULT_P5_SCRIPT)
25
+
26
+ // Keep the animationURL (for the preview) up to date
27
+ watchEffect(() => {
28
+ animationUrl.value = getP5HtmlUri('Preview', script.value)
29
+ })
30
+
31
+ // Encode the artifact as per how the P5Renderer.sol contract expects it.
32
+ watchEffect(() => {
33
+ artifact.value = encodeAbiParameters(
34
+ [ { type: 'string', name: 'image' }, { type: 'string', name: 'script' } ],
35
+ [ image.value, script.value ],
36
+ )
37
+ })
38
+ </script>
39
+
40
+ <style>
41
+ .mint-renderer-p5 {
42
+ padding: 0 !important;
43
+ border: 0 !important;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 0;
47
+
48
+ > .tabs {
49
+ height: min-content;
50
+ }
51
+
52
+ > .tabs-content {
53
+ border: var(--border);
54
+ border-top: 0;
55
+ height: 100%;
56
+
57
+ > * {
58
+ height: 100%;
59
+ }
60
+
61
+ > *:not(.full) {
62
+ padding: var(--spacer);
63
+ }
64
+ }
65
+ }
66
+ </style>
67
+
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <aside class="mint-select-renderer">
3
+ <select
4
+ class="select"
5
+ v-model="selection"
6
+ >
7
+ <option
8
+ v-for="( renderer, index ) in collection.renderers"
9
+ :value="index"
10
+ :title="renderer.name"
11
+ >{{ renderer.name }}</option>
12
+ <option disabled>----</option>
13
+ <option value="new">Install New</option>
14
+ </select>
15
+ </aside>
16
+ </template>
17
+
18
+ <script setup>
19
+ const { collection } = defineProps(['collection'])
20
+ const id = useArtistId()
21
+
22
+ const store = useOnchainStore()
23
+ onMounted(() => store.fetchCollectionRenderers(collection.address))
24
+
25
+ const { renderer, reset } = useCreateMintData()
26
+ const selection = ref(renderer.value)
27
+ watch(selection, () => {
28
+ if (selection.value === 'new') {
29
+ return navigateTo({ name: 'id-collection-renderers', params: { id: id.value, collection: collection.address } })
30
+ }
31
+
32
+ renderer.value = selection.value
33
+
34
+ reset()
35
+ })
36
+ </script>
37
+
38
+ <style scoped>
39
+ .mint-select-renderer {
40
+ display: grid;
41
+ gap: var(--spacer);
42
+
43
+ @media (--md) {
44
+ display: flex;
45
+ justify-content: space-between;
46
+
47
+ .select {
48
+ width: fit-content;
49
+ }
50
+ }
51
+ }
52
+ </style>
@@ -9,7 +9,7 @@
9
9
  :renderer="renderer"
10
10
  >
11
11
  <template #after>
12
- <RendererInstallRendererButton
12
+ <RendererInstallButton
13
13
  :collection="collection"
14
14
  :renderer="renderer"
15
15
  #after
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div>
6
6
  <RendererOverviewCard
7
- v-for="renderer of collection.renderers"
7
+ v-for="renderer of installedRenderers"
8
8
  :renderer="renderer"
9
9
  />
10
10
  </div>
@@ -20,7 +20,7 @@
20
20
  >
21
21
  <template #after>
22
22
  <div class="actions">
23
- <RendererInstallRendererButton
23
+ <RendererInstallButton
24
24
  :collection="collection"
25
25
  :renderer="renderer"
26
26
  />
@@ -38,19 +38,21 @@
38
38
  </template>
39
39
 
40
40
  <script setup>
41
- const { collection } = defineProps(['collection'])
41
+ const props = defineProps(['collection'])
42
42
 
43
43
  const appConfig = useAppConfig()
44
44
  const store = useOnchainStore()
45
45
 
46
+ const installedRenderers = computed(() => props.collection.renderers)
47
+
46
48
  const availableRenderers = computed(
47
49
  () => appConfig.knownRenderers.filter(r =>
48
- !collection.renderers.map(cr => cr.address).includes(r.address)
50
+ !installedRenderers.value.map(cr => cr.address).includes(r.address)
49
51
  )
50
52
  )
51
53
 
52
54
  onMounted(() => {
53
- store.fetchCollectionRenderers(collection.address)
55
+ store.fetchCollectionRenderers(props.collection.address)
54
56
  })
55
57
  </script>
56
58
 
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <menu class="tabs">
3
+ <slot name="menu" :active="active" :select="select" />
4
+ </menu>
5
+
6
+ <section class="tabs-content">
7
+ <slot name="content" :active="active" />
8
+ </section>
9
+ </template>
10
+
11
+ <script setup>
12
+ const props = defineProps(['initial'])
13
+
14
+ const active = ref(props.initial)
15
+ const select = k => active.value = k
16
+ </script>
17
+
18
+ <style>
19
+ .tabs {
20
+ border-bottom: var(--border);
21
+ padding: 0 var(--size-1);
22
+ margin: 0;
23
+ display: flex;
24
+ gap: var(--size-1);
25
+
26
+ > .button {
27
+ margin-bottom: calc(-1 * var(--border-width));
28
+ border-bottom-left-radius: 0;
29
+ border-bottom-right-radius: 0;
30
+
31
+ &.active {
32
+ border-color: var(--border-color);
33
+ border-bottom-color: var(--background);
34
+ }
35
+ }
36
+ }
37
+ </style>
38
+
@@ -2,7 +2,8 @@
2
2
  <article class="token-detail">
3
3
  <div class="artifact">
4
4
  <div>
5
- <Image v-if="token.artifact" :src="token.artifact" :alt="token.name" />
5
+ <Embed v-if="token.animationUrl" :src="token.animationUrl" />
6
+ <Image v-else-if="token.image" :src="token.image" :alt="token.name" />
6
7
  <ImageVoid v-else />
7
8
  </div>
8
9
  </div>
@@ -32,7 +32,8 @@
32
32
  <p v-if="mintOpen" class="closes-in">Closes in {{ blocksRemaining }} {{ pluralize('block', Number(blocksRemaining))}}</p>
33
33
  <p v-else class="closed-at">Closed at block {{ token.untilBlock }}</p>
34
34
  </header>
35
- <Image v-if="token.artifact" :src="token.artifact" :alt="token.name" />
35
+ <Embed v-if="token.animationUrl" :src="token.animationUrl" />
36
+ <Image v-else-if="token.image" :src="token.image" :alt="token.name" />
36
37
  <ImageVoid v-else />
37
38
  <CardLink :to="{
38
39
  name: 'id-collection-tokenId',
@@ -3,7 +3,7 @@ import { type GetBalanceReturnType } from '@wagmi/core'
3
3
  import { parseAbiItem, type PublicClient } from 'viem'
4
4
  import type { MintEvent } from '~/utils/types'
5
5
 
6
- export const CURRENT_STATE_VERSION = 5
6
+ export const CURRENT_STATE_VERSION = 6
7
7
  export const MAX_BLOCK_RANGE = 1800n
8
8
  export const MINT_BLOCKS = BLOCKS_PER_DAY
9
9
 
@@ -320,7 +320,7 @@ export const useOnchainStore = () => {
320
320
  console.info(`Fetching token #${tokenId}`)
321
321
 
322
322
  const [data, untilBlock] = await Promise.all([
323
- mintContract.read.uri([tokenId], { gas: 10_000_000_000 }) as Promise<string>,
323
+ mintContract.read.uri([tokenId], { gas: 100_000_000_000 }) as Promise<string>,
324
324
  mintContract.read.mintOpenUntil([tokenId]) as Promise<bigint>,
325
325
  ])
326
326
 
@@ -332,7 +332,9 @@ export const useOnchainStore = () => {
332
332
  collection: address,
333
333
  name: metadata.name,
334
334
  description: metadata.description,
335
- artifact: metadata.image,
335
+ image: metadata.image,
336
+ animationUrl: metadata.animation_url,
337
+ scriptUrl: metadata.script_url,
336
338
  untilBlock,
337
339
  mintsBackfilledUntilBlock: 0n,
338
340
  mintsFetchedUntilBlock: 0n,
@@ -340,7 +342,9 @@ export const useOnchainStore = () => {
340
342
  }
341
343
 
342
344
  this.collections[address].tokens[`${token.tokenId}`] = token
343
- } catch (e) { }
345
+ } catch (e) {
346
+ console.error(e)
347
+ }
344
348
  },
345
349
 
346
350
  async fetchTokenBalance (token: Token, address: `0x${string}`) {
@@ -10,8 +10,8 @@ const image = ref('')
10
10
  const animationUrl = ref('')
11
11
 
12
12
  // Renderer data
13
- const renderer = ref(0)
14
- const extraData = ref(0n)
13
+ const renderer: Ref<number> = ref(0)
14
+ const extraData: Ref<bigint> = ref(0n)
15
15
 
16
16
  // Main token creation composable
17
17
  export const useCreateMintData = () => {
@@ -24,7 +24,6 @@ export const useCreateMintData = () => {
24
24
  image.value = ''
25
25
  animationUrl.value = ''
26
26
 
27
- renderer.value = 0
28
27
  extraData.value = 0n
29
28
  }
30
29
 
@@ -41,6 +40,24 @@ export const useCreateMintData = () => {
41
40
  }
42
41
  }
43
42
 
43
+ // Expose the mint component based on the selected renderer
44
+ export const useCreateMintRendererComponent = (collection: Collection) => {
45
+ const appConfig = useAppConfig()
46
+ const rendererAddress: Ref<string | null> = computed(() => {
47
+ if (! collection.renderers?.length) return null
48
+
49
+ return collection.renderers[renderer.value].address.toLowerCase()
50
+ })
51
+
52
+ const component = computed(() => appConfig.knownRenderers
53
+ .find((r: Renderer) => r.address.toLowerCase() === rendererAddress.value)?.component || 'Base'
54
+ )
55
+
56
+ return {
57
+ component,
58
+ }
59
+ }
60
+
44
61
  // Token creation flow
45
62
  export const useCreateMintFlow = (collection: Collection, txFlow: Ref) => {
46
63
  const { $wagmi } = useNuxtApp()
@@ -99,7 +116,7 @@ export const useCreateMintFlow = (collection: Collection, txFlow: Ref) => {
99
116
  name.value,
100
117
  description.value,
101
118
  multiTransactionPrepare ? [] : artifactByteArray,
102
- 0, // Renderer
119
+ renderer.value,
103
120
  0n, // Additional Data
104
121
  ],
105
122
  }))
package/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { Renderer } from './utils/types'
3
3
  declare module 'nuxt/schema' {
4
4
  interface AppConfigInput {
5
5
  // Known renderers besides the base renderer
6
- renderers: Renderer[],
6
+ knownRenderers: Renderer[],
7
7
  }
8
8
  }
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visualizevalue/mint-app-base",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "dependencies": {
@@ -14,6 +14,8 @@
14
14
  "@wagmi/core": "^2.13.5",
15
15
  "@wagmi/vue": "^0.0.40",
16
16
  "buffer": "^6.0.3",
17
+ "codemirror": "^5",
18
+ "codemirror-editor-vue3": "^2.8.0",
17
19
  "multiformats": "^13.2.2",
18
20
  "postcss-custom-media": "^10.0.6",
19
21
  "postcss-custom-selectors": "^7.1.10",
@@ -25,6 +27,7 @@
25
27
  "@visualizevalue/mint-utils": "^0.0.3"
26
28
  },
27
29
  "devDependencies": {
30
+ "@types/codemirror": "^5.60.15",
28
31
  "nuxt": "^3.13.2",
29
32
  "typescript": "^5.5.4"
30
33
  },
@@ -9,10 +9,6 @@
9
9
  </template>
10
10
 
11
11
  <script setup>
12
- // Reset any previously set data on initial load
13
- const { reset } = useCreateMintData()
14
- onMounted(() => reset())
15
-
16
12
  // Prepare breadcrumbs
17
13
  const props = defineProps(['collection'])
18
14
  const collection = computed(() => props.collection)
@@ -0,0 +1,33 @@
1
+ export const DEFAULT_P5_SCRIPT =
2
+ `let dimension
3
+
4
+ function setup() {
5
+ dimension = Math.min(windowWidth, windowHeight)
6
+
7
+ createCanvas(dimension, dimension)
8
+
9
+ background(150)
10
+ }
11
+
12
+ function draw() {
13
+ // ...
14
+ }
15
+ `
16
+
17
+ export const getP5Html = (title: string, script: string, isUri: boolean = false) =>
18
+ `<html>
19
+ <head>
20
+ <title>${title}</title>
21
+ <link rel="stylesheet" href="data:text/css;base64,aHRtbHtoZWlnaHQ6MTAwJX1ib2R5e21pbi1oZWlnaHQ6MTAwJTttYXJnaW46MDtwYWRkaW5nOjB9Y2FudmFze3BhZGRpbmc6MDttYXJnaW46YXV0bztkaXNwbGF5OmJsb2NrO3Bvc2l0aW9uOmFic29sdXRlO3RvcDowO2JvdHRvbTowO2xlZnQ6MDtyaWdodDowfQ==">
22
+ </head>
23
+ <body>
24
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js"></script>
25
+ ${
26
+ isUri ? `<script src="${script}"></script>` : `<script>${script}</script>`
27
+ }
28
+ </body>
29
+ </html>`
30
+
31
+ export const getP5HtmlUri = (title: string, script: string, isUri: boolean = false) =>
32
+ `data:text/html;base64,${Buffer.from(getP5Html(title, script, isUri)).toString('base64')}`
33
+
package/utils/types.ts CHANGED
@@ -44,7 +44,9 @@ export interface Token {
44
44
  tokenId: bigint
45
45
  name: string
46
46
  description: string
47
- artifact: string
47
+ image: string,
48
+ animationUrl?: string,
49
+ scriptUrl?: string,
48
50
  untilBlock: bigint
49
51
  mintsFetchedUntilBlock: bigint
50
52
  mintsBackfilledUntilBlock: bigint
@@ -66,5 +68,6 @@ export interface Renderer {
66
68
  address: `0x${string}`
67
69
  name: string
68
70
  description?: string
71
+ component?: string
69
72
  version: bigint
70
73
  }