@visualizevalue/mint-app-base 0.1.48 → 0.1.49
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 +3 -1
- package/components/CodeEditor.client.vue +49 -0
- package/components/Collection/Actions.vue +0 -7
- package/components/Embed.vue +33 -0
- package/components/Form/SelectFile.vue +4 -0
- package/components/Mint/Detail.client.vue +22 -6
- package/components/Mint/Preview.client.vue +20 -5
- package/components/Mint/Renderer/Base.client.vue +28 -13
- package/components/Mint/Renderer/P5.client.vue +67 -0
- package/components/Mint/SelectRenderer.client.vue +52 -0
- package/components/Renderer/InstallCustom.client.vue +1 -1
- package/components/Renderer/Overview.client.vue +7 -5
- package/components/Tabs.vue +37 -0
- package/components/Token/Detail.client.vue +2 -1
- package/components/Token/OverviewCard.vue +2 -1
- package/composables/collections.ts +8 -4
- package/composables/createMint.ts +21 -4
- package/index.d.ts +1 -1
- package/package.json +4 -1
- package/pages/[id]/[collection]/mint.vue +0 -4
- package/utils/p5Script.ts +33 -0
- package/utils/types.ts +4 -1
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: '
|
|
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
|
+
|
|
@@ -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
|
-
<
|
|
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
|
-
<
|
|
4
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
45
|
-
const isSmall = computed(() =>
|
|
52
|
+
const imageSize = ref(0)
|
|
53
|
+
const isSmall = computed(() => imageSize.value / 1024 < 10)
|
|
46
54
|
const setArtifact = async (file) => {
|
|
47
55
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
image.value = await imageFileToDataUri(file)
|
|
57
|
+
imageSize.value = file.size
|
|
50
58
|
} catch (e) {
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
66
|
+
image.value = ''
|
|
59
67
|
} else {
|
|
60
|
-
|
|
68
|
+
image.value = ipfsToHttpURI(`ipfs://${validated}`)
|
|
61
69
|
}
|
|
62
70
|
})
|
|
63
71
|
watch(arTxId, () => {
|
|
64
|
-
|
|
72
|
+
image.value = `https://arweave.net/${arTxId.value}`
|
|
65
73
|
})
|
|
66
|
-
watch(mode, () =>
|
|
74
|
+
watch(mode, () => image.value = '')
|
|
75
|
+
|
|
76
|
+
watch(image, () => {
|
|
77
|
+
if (props.decoupleArtifact) return
|
|
67
78
|
|
|
68
|
-
//
|
|
69
|
-
|
|
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>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<div>
|
|
6
6
|
<RendererOverviewCard
|
|
7
|
-
v-for="renderer of
|
|
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
|
-
<
|
|
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
|
|
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
|
-
!
|
|
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,37 @@
|
|
|
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
|
+
/* border-bottom-color: transparent; */
|
|
28
|
+
margin-bottom: calc(-1 * var(--border-width));
|
|
29
|
+
|
|
30
|
+
&.active {
|
|
31
|
+
border-color: var(--border-color);
|
|
32
|
+
border-bottom-color: var(--background);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
</style>
|
|
37
|
+
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
<article class="token-detail">
|
|
3
3
|
<div class="artifact">
|
|
4
4
|
<div>
|
|
5
|
-
<
|
|
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
|
-
<
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
119
|
+
renderer.value,
|
|
103
120
|
0n, // Additional Data
|
|
104
121
|
],
|
|
105
122
|
}))
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visualizevalue/mint-app-base",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
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
|
-
|
|
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
|
}
|