@visualizevalue/mint-app-base 0.1.116 → 0.1.117

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
@@ -41,6 +41,13 @@ export default defineAppConfig({
41
41
  address: '0x901603b81aae5eb2a1dc3cec77280bf6e4727bfe',
42
42
  description: 'The default renderer',
43
43
  },
44
+ {
45
+ component: 'Markdown',
46
+ name: 'Markdown Renderer',
47
+ version: 1n,
48
+ address: '0x31e819f6a2fdf77af11648b6d571416aa48eb650',
49
+ description: 'Renders markdown content as onchain text artifacts',
50
+ },
44
51
  {
45
52
  component: 'P5',
46
53
  name: 'P5 Renderer (Sepolia)',
@@ -17,6 +17,7 @@ import CodeMirror from 'codemirror-editor-vue3'
17
17
  import 'codemirror/addon/display/placeholder.js'
18
18
  import 'codemirror/mode/htmlmixed/htmlmixed.js'
19
19
  import 'codemirror/mode/javascript/javascript.js'
20
+ import 'codemirror/mode/markdown/markdown.js'
20
21
 
21
22
  const props = defineProps({
22
23
  modelValue: String,
@@ -2,12 +2,14 @@
2
2
  import Animation from './Renderer/Animation.client.vue'
3
3
  import Base from './Renderer/Base.client.vue'
4
4
  import Code from './Renderer/Code.client.vue'
5
+ import Markdown from './Renderer/Markdown.client.vue'
5
6
  import P5 from './Renderer/P5.client.vue'
6
7
 
7
8
  const components = {
8
9
  Animation,
9
10
  Base,
10
11
  Code,
12
+ Markdown,
11
13
  P5,
12
14
  }
13
15
 
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div class="mint-renderer-markdown">
3
+ <div class="fields">
4
+ <FormInput v-model="name" :placeholder="$t('mint.base.title_placeholder')" required />
5
+ <FormInput v-model="description" :placeholder="$t('mint.base.description_placeholder')" />
6
+ </div>
7
+
8
+ <CodeEditor v-model="markdown" mode="text/x-markdown" :placeholder="$t('mint.markdown.placeholder')" class="full" />
9
+ </div>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { watchDebounced } from '@vueuse/core'
14
+ import { getMarkdownSvgUri, getMarkdownHtmlUri } from '~/utils/markdown'
15
+
16
+ const { artifact, image, animationUrl, name, description } = useCreateMintData()
17
+
18
+ const markdown = ref('')
19
+
20
+ const updatePreview = () => {
21
+ if (!markdown.value) {
22
+ image.value = ''
23
+ animationUrl.value = ''
24
+ return
25
+ }
26
+
27
+ image.value = getMarkdownSvgUri(name.value || '', markdown.value)
28
+ animationUrl.value = getMarkdownHtmlUri(markdown.value)
29
+ }
30
+ watchDebounced([markdown, name], updatePreview, { debounce: 500, maxWait: 3000 })
31
+
32
+ // The MarkdownRenderer contract stores raw markdown bytes (not ABI-encoded).
33
+ watchEffect(() => {
34
+ artifact.value = markdown.value
35
+ })
36
+ </script>
37
+
38
+ <style>
39
+ .mint-renderer-markdown {
40
+ padding: 0 !important;
41
+ border: 0 !important;
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 0;
45
+ overflow: hidden;
46
+ width: 100%;
47
+
48
+ >.fields {
49
+ display: flex;
50
+ flex-direction: column;
51
+ gap: var(--spacer);
52
+ padding: var(--spacer);
53
+ border: var(--border);
54
+ border-radius: var(--card-border-radius);
55
+ }
56
+
57
+ >.code-editor {
58
+ border: var(--border);
59
+ border-top: none;
60
+ border-radius: var(--card-border-radius);
61
+ overflow: hidden;
62
+ min-height: 24rem;
63
+ }
64
+ }
65
+ </style>
package/locales/en.json CHANGED
@@ -54,6 +54,9 @@
54
54
  "static": "Static",
55
55
  "script": "Script"
56
56
  },
57
+ "markdown": {
58
+ "placeholder": "Write your markdown here..."
59
+ },
57
60
  "p5": {
58
61
  "static": "Static",
59
62
  "script": "P5 Script"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visualizevalue/mint-app-base",
3
- "version": "0.1.116",
3
+ "version": "0.1.117",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "dependencies": {
@@ -18,12 +18,13 @@ const load = async () => {
18
18
  try {
19
19
  await store.fetchToken(collection.value.address, route.params.tokenId)
20
20
  } catch (e) {
21
- navigateTo({ name: 'id-collection' }, { replace: true })
21
+ console.debug(`Error`, e, `Redirecting to collection`)
22
+ return navigateTo({ name: 'id-collection' }, { replace: true })
22
23
  }
23
24
 
24
25
  loading.value = false
25
26
  }
26
27
 
27
28
  onMounted(() => load())
28
- watch(route, () => load())
29
+ watch(() => route.params.tokenId, () => load())
29
30
  </script>
@@ -22,7 +22,7 @@ const load = async () => {
22
22
  throw new Error('Invalid owner')
23
23
  }
24
24
  } catch (e) {
25
- navigateTo({ name: 'id' }, { replace: true })
25
+ return navigateTo({ name: 'id' }, { replace: true })
26
26
  }
27
27
 
28
28
  loading.value = false
@@ -0,0 +1,50 @@
1
+ const MAX_PREVIEW_LENGTH = 800
2
+
3
+ const escapeXml = (str: string) => str
4
+ .replace(/&/g, '&amp;')
5
+ .replace(/</g, '&lt;')
6
+ .replace(/>/g, '&gt;')
7
+ .replace(/"/g, '&quot;')
8
+ .replace(/'/g, '&apos;')
9
+
10
+ const toBase64DataUri = (mime: string, content: string) =>
11
+ `data:${mime};base64,${btoa(unescape(encodeURIComponent(content)))}`
12
+
13
+ /**
14
+ * Generate an SVG preview matching the on-chain MarkdownRenderer output.
15
+ */
16
+ export const getMarkdownSvg = (title: string, content: string) => {
17
+ const truncated = content.length > MAX_PREVIEW_LENGTH
18
+ ? content.slice(0, MAX_PREVIEW_LENGTH) + '...'
19
+ : content
20
+
21
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">` +
22
+ `<rect width="400" height="400" fill="#111"/>` +
23
+ `<text x="20" y="36" font-family="monospace" font-size="18" font-weight="bold" fill="white">${escapeXml(title)}</text>` +
24
+ `<line x1="20" y1="50" x2="380" y2="50" stroke="#333" stroke-width="1"/>` +
25
+ `<foreignObject x="20" y="60" width="360" height="320">` +
26
+ `<div xmlns="http://www.w3.org/1999/xhtml" style="` +
27
+ `font-family:monospace;font-size:12px;color:#999;` +
28
+ `white-space:pre-wrap;word-break:break-word;` +
29
+ `overflow:hidden;height:320px;` +
30
+ `mask-image:linear-gradient(to bottom,black 60%,transparent 100%);` +
31
+ `-webkit-mask-image:linear-gradient(to bottom,black 60%,transparent 100%)` +
32
+ `">${escapeXml(truncated)}</div>` +
33
+ `</foreignObject>` +
34
+ `</svg>`
35
+ }
36
+
37
+ export const getMarkdownSvgUri = (title: string, content: string) =>
38
+ toBase64DataUri('image/svg+xml', getMarkdownSvg(title, content))
39
+
40
+ /**
41
+ * Generate a simple HTML preview for the markdown content.
42
+ */
43
+ export const getMarkdownHtml = (content: string) =>
44
+ `<!DOCTYPE html><html><head><meta charset="utf-8"><style>` +
45
+ `*{margin:0;padding:0;box-sizing:border-box}` +
46
+ `body{font-family:monospace;font-size:14px;color:#999;background:#111;padding:2rem;white-space:pre-wrap;word-break:break-word}` +
47
+ `</style></head><body>${escapeXml(content)}</body></html>`
48
+
49
+ export const getMarkdownHtmlUri = (content: string) =>
50
+ toBase64DataUri('text/html', getMarkdownHtml(content))