Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add support for text mints #411

Merged
merged 30 commits into from
Jun 6, 2024

Conversation

kittypurrry
Copy link
Contributor

@kittypurrry kittypurrry commented Apr 14, 2024

This PR adds support for ASCII text mints. There are two new options added to the MintForm component - This is a text mint checkbox and a monospace font required checkbox.

For typed mints, the cover and thumbnail images are automatically generated based on the text input and the font selected. The description will also automatically be replaced by the text mint input.

Minting Form
The textarea renders the monospace (IBM Plex Mono) / non-monospace font (Source Sans Pro) depending on the monospace font required checkbox value.

Monospace:
image

Non-monospace:
image

Preview / Token Page
Example: /objkt/850488

The preview component displays the typed art, in either a monospace font a non-monospace font (Source Sans Pro). If Monospace is selected, a 'monospace' tag is automatically added to ensure that it renders properly on OBJKT; and viceversa.

image

Feed Page

Text NFTs are rendered as their cover/thumbnail images on the feed listing page, similar to how OBJKT does it. The actual text mint is only rendered as in the individual token page.

image

Notes

  • Source Sans Pro was selected for text mints as it's the default font (non-mono) on OBJKT. This ensures text are encoded correctly and displayed as intended on OBJKT and Teia.
  • IBM Plex Mono was selected for monospace text mints as it was the font used by typed.art. Currently, it seems that OBJKT does not provide much monospace support for text mints, and a feature request has been raised here: https://roadmap.objkt.com/b/software-feature-request/use-a-monospaced-font-for-text-tokens/. As a result, do expect that some of the monospace typed art would appear broken on OBJKT. However, Teia has taken care of it by rendering the right fonts for your monospace typed art. See an example minted type mint on OBJKT: https://objkt.com/tokens/hicetnunc/850488

--

Update [21/04]:

  • UltimateTextToImage package has been uninstalled, replace by using node-canvas as done in typed.art._
  • Moved 'This is a text mint' checkbox to be before description, so that when checked, the description box is automatically hidden and replaced by the text mint input itself.

Non-text mint
image

Text mint checked
image

Update [05/05]:

  • Change Mono from IBM Plex Mono to Iosevka

kittypurrry and others added 14 commits March 28, 2024 19:58
- Create new textarea input component for typed based nfts that renders the correct fonts to be displayed correctly on OBJKT
- Modify existing FormFields to include new select options for user to specify if it's a typed nft and whether it requires monospace font
- Auto generate cover image based on typed input
- Description will be automatially be the typed input; hide description field
- Rename Monospace checkbox field
@melMass
Copy link
Member

melMass commented May 2, 2024

Thanks, almost good for me, besides the comment I just made I have a few questions:

  • Why not use ioveska for .mono?
  • I didn't follow the discussions that lead to this PR so I just want to be sure it was talked about with someone at OBJKT.com?

We can clean it up more in a separate PR, but fI think it's mostly fine for now if the metadata are good for everyone

Comment on lines 119 to 214
export const generateTypedArtCoverImage = async (
txt: string,
monospace: boolean
) => {
const font = monospace ? 'IBM Plex Mono' : 'Source Sans Pro'
const cv_font = `16px ${font}`
if (txt.length === 0) {
return false
}
const createContext = function (width: number, height: number) {
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')
if (!ctx) throw new Error('Could not create canvas context')
return ctx
}
const c = createContext(512, 512)
c.filter = 'grayscale(100%)'

let dize = []
const lines = txt.split('\n')
dize = lines
c.font = cv_font
const x = 0
const y = 16
const lineheight = 16
const result = dize.reduce(
(r, e) => (c.measureText(r).width < c.measureText(e).width ? e : r),
''
)
if (result.length > 130) {
c.canvas.width = 512
c.canvas.height = 512
} else {
c.canvas.width = c.measureText(result).width
c.canvas.height = lineheight * dize.length + 12
}
c.fillStyle = 'transparent'
c.fillRect(0, 0, c.canvas.width, c.canvas.height)
c.filter = 'grayscale(100%)'
c.fillStyle = 'white'
if (c.canvas.width === 512) {
const s = lines[0].substring(0, 20)
c.fillText(s + '...', 16, 256)
} else {
for (let i = 0; i < lines.length; i++) {
c.fillText(lines[i], x, y + i * lineheight)
}
}
const ca = createContext(512, 512)
ca.fillStyle = 'transparent'
ca.fillRect(0, 0, 512, 512)
const cerceve = createContext(512, 512)
const cImage = new Image()
const img = new Image()
return new Promise((resolve, reject) => {
img.src = c.canvas.toDataURL('svg')
img.onload = function () {
ca.imageSmoothingEnabled = true
ca.width = 512
ca.height = 512
const hRatio = ca.width / img.width
const vRatio = ca.height / img.height
const ratio = Math.min(hRatio, vRatio)
const centerShift_x = (ca.width - img.width * ratio) / 2
const centerShift_y = (ca.height - img.height * ratio) / 2
ca.drawImage(
img,
0,
0,
img.width,
img.height,
centerShift_x,
centerShift_y,
img.width * ratio,
img.height * ratio
)
const t = ca.canvas.toDataURL('svg')
cImage.src = t
cImage.onload = async function () {
cerceve.fillStyle = 'transparent'
cerceve.fillRect(0, 0, 512, 512)
cerceve.drawImage(cImage, 0, 0, 512, 512, 16, 16, 512 - 16, 512 - 16)
const ctx = cerceve.canvas.toDataURL('svg')
const res = await fetch(ctx)
const blob = await res.blob()
const file = new File([blob], 'Generated Cover.png', {
type: 'image/png',
})

resolve(file)
}
}
})
}
Copy link
Member

@melMass melMass May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you simplify/refactor this a bit for clarity? I moved the text.length check first to un-nest it, changed ts types and use only async/await but I'm still not fully getting the logic of it.

@melMass
Copy link
Member

melMass commented May 5, 2024

Regarding the metadata, it follows typedart logic (minus the on chain text) so it's actually fine!

@melMass melMass force-pushed the feat/typed-nft branch 2 times, most recently from d3cf78e to 74bde4d Compare May 11, 2024 10:57
@melMass
Copy link
Member

melMass commented May 11, 2024

When checking the generated previews I get weird results:

https://cache.teia.rocks/ipfs/QmYdXkZM2nCX38gwrdsD21VG1Vq2P4eur1DgDMu3aLocWZ
https://cache.teia.rocks/ipfs/QmXnZrk9u4KUR9bWNem4oQQS6VpuCuS2FXFDosRqExAeww

Another thing is that it's currently not handling single long lines and I'm not sure what we want to happen in these cases:

input text

In the realm of decentralization, power disperses like scattered light,freedom blooms in every node, no central might.Trust in the collective, no single hand's sway, innovation thrives in this distributed array. From governance to finance, it's a paradigm shift, where autonomy reigns, and control takes its lift.

image

Not coming from that PR directly but revealed by it.
Also adds ts,tsx and json to the linters.
Mainly simplifying code, reusing variables.

This also adds a preview of the generation to the mint
form directly.
@melMass
Copy link
Member

melMass commented May 26, 2024

Thanks Ziroh
There is one edge case that i think needs testing (cc @merchantcoppola) very long text (for the css thing that you just added)

@Zir0h Zir0h merged commit af60898 into teia-community:main Jun 6, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants