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
Merged
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9497134
feat: Add support for typed based nfts
kittypurrry Mar 28, 2024
a5f11b0
Some updates to display token's displayuri instead of artifact
kittypurrry Apr 7, 2024
3ddd0b6
Update Monotype art to use 'IBM Mono Plex' to be consistent with type…
kittypurrry Apr 7, 2024
8aa8566
Remove monaco
kittypurrry Apr 7, 2024
9baff04
Merge branch 'teia-community:main' into feat/typed-nft
kittypurrry Apr 8, 2024
4328cc0
Fix lint errors
kittypurrry Apr 13, 2024
1c48db3
Merge branch 'teia-community:main' into feat/typed-nft
kittypurrry Apr 14, 2024
8fef7bc
Fix some UI issues while minting - switching between preview and edit…
kittypurrry Apr 14, 2024
9616958
Minor UI updates for very long text mints
kittypurrry Apr 14, 2024
6424135
Some minor adjustments
kittypurrry Apr 16, 2024
4e81265
Replace UltimateTextToImage with canvas solution
kittypurrry Apr 21, 2024
629db11
Merge branch 'main' into feat/typed-nft
kittypurrry Apr 21, 2024
b219adf
chore: ♻️ apply format
melMass May 2, 2024
3b836bf
fix: ✨ minor edits
melMass May 2, 2024
c7feab5
Fix font issue for generated thumbnail and changed mono font to Iosevka
kittypurrry May 5, 2024
aaea30c
Add comments and refactor thumbnail generation a bit
kittypurrry May 5, 2024
5777d40
Update char length before ellipsis since text size can accomodate for…
kittypurrry May 5, 2024
25ab072
Merge branch 'main' into feat/typed-nft
melMass May 11, 2024
b993686
style: 💎 fix various styles issues
melMass May 11, 2024
dbcf13d
chore: 🧹 convert typed_art utils to ts
melMass May 11, 2024
324478f
refactor: 📦 image generation logic
melMass May 11, 2024
9f21675
fix: 🐛 issues reported by CI
melMass May 11, 2024
1ebe8a1
fix: preview generation
melMass May 20, 2024
45e243e
chore: 🧹 prefer const
melMass May 20, 2024
8934d88
hide scrollbars and add text feed to the menu
Zir0h May 26, 2024
f5afa3c
disable description in preview cards for text/plain tokens
Zir0h May 26, 2024
7190069
use overflow:scroll
Zir0h Jun 5, 2024
e15a53f
mess around with overflow / whitespace
Zir0h Jun 6, 2024
481c8cf
Merge branch 'main' into feat/typed-nft
Zir0h Jun 6, 2024
747143a
added padding
Zir0h Jun 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: preview generation
  • Loading branch information
melMass committed May 20, 2024
commit 1ebe8a11332578ef62fbfeee41e2522d7e6adeba
1 change: 1 addition & 0 deletions src/components/form/index.module.scss
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
}

.typed_preview {
width: 100%;
border: 1px solid var(--border-color);
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
172 changes: 83 additions & 89 deletions src/utils/mint.ts
Original file line number Diff line number Diff line change
@@ -125,16 +125,14 @@
*/
export const generateTypedArtCoverImage = async (
text: string,
monospace: boolean
monospace: boolean,
size: number,
horizontalAlign: boolean
): Promise<File> => {
if (!text || text.length === 0) {
if (!text || text.trim().length === 0) {
throw new Error('Input text must not be empty')
}

const font = monospace ? 'Iosevka' : 'Source Sans Pro'
const font_size = 16
const cv_font = `${font_size}px ${font}`

size = size || 1024
const createCanvasContext = (
width: number,
height: number
@@ -147,106 +145,102 @@
return ctx
}

const size = 1024
const textCtx = createCanvasContext(size, size)
const font = monospace ? 'Iosevka' : 'Source Sans Pro'
const fontRatio = 0.75
const minFontSize = 16
let fontSize = Math.max(minFontSize, size * fontRatio)

const cv_font = `${fontSize}px ${font}`

const finalCtx = createCanvasContext(size, size)
finalCtx.font = cv_font
finalCtx.filter = 'grayscale(100%)'
finalCtx.fillStyle = 'transparent'
finalCtx.fillRect(0, 0, size, size)
finalCtx.fillStyle = 'white'

let lines = text.split('\n').filter((line, index, array) => {
return (
line.trim() !== '' ||
index === array.length - 1 ||
array[index + 1].trim() !== ''
)
})

textCtx.font = cv_font
textCtx.filter = 'grayscale(100%)'
const margin = 50
const maxWidth = size - margin * 2
const maxHeight = size - margin * 2

const lines = text.split('\n')
const longestLine = lines.reduce(
let longestLine = lines.reduce(

Check failure on line 174 in src/utils/mint.ts

GitHub Actions / build (18)

'longestLine' is never reassigned. Use 'const' instead

Check failure on line 174 in src/utils/mint.ts

GitHub Actions / build (18)

'longestLine' is never reassigned. Use 'const' instead
(longest, line) =>
textCtx.measureText(line).width > textCtx.measureText(longest).width
finalCtx.measureText(line).width > finalCtx.measureText(longest).width
? line
: longest,
''
)

textCtx.canvas.width = Math.min(textCtx.measureText(longestLine).width, size)
textCtx.canvas.height = Math.min(font_size * lines.length + font_size, size)
let lineHeight =
finalCtx.measureText(')').actualBoundingBoxAscent +
finalCtx.measureText(')').actualBoundingBoxDescent +
5
let lineWidth = finalCtx.measureText(longestLine).width

textCtx.fillStyle = 'transparent'
// textCtx.fillStyle = 'black'// for debugging
textCtx.fillRect(0, 0, textCtx.canvas.width, textCtx.canvas.height)
textCtx.filter = 'grayscale(100%)'
textCtx.fillStyle = 'white'
while (lines.length * lineHeight > maxHeight || lineWidth > maxWidth) {
fontSize -= 1
finalCtx.font = `${fontSize}px ${font}`

//NOTE: yes this is required twice... not sure why
textCtx.font = cv_font
if (fontSize <= minFontSize) {
break
}

if (textCtx.canvas.width === size) {
const truncatedFirstLine = lines[0].substring(0, 20)
textCtx.fillText(truncatedFirstLine + '...', 0, font_size)
} else {
const x = 0
const y = font_size
const lineHeight = font_size
lineWidth = finalCtx.measureText(longestLine).width
lineHeight =
finalCtx.measureText(')').actualBoundingBoxAscent +
finalCtx.measureText(')').actualBoundingBoxDescent +
5
}

lines.forEach((line, index) =>
textCtx.fillText(line, x, y + index * lineHeight)
)
if (lineWidth > maxWidth) {
lines = [
text.split('\n')[0].substring(0, Math.floor(maxWidth / fontSize) - 3) +
'...',
]
}
lineHeight =
finalCtx.measureText(')').actualBoundingBoxAscent +
finalCtx.measureText(')').actualBoundingBoxDescent +
5

const scaledCtx = createCanvasContext(size, size)
scaledCtx.fillStyle = 'transparent'
scaledCtx.fillRect(0, 0, size, size)
const totalTextHeight = lines.length * lineHeight
const baselineOffset = (size - totalTextHeight) / 2

const finalCtx = createCanvasContext(size, size)
const coverImage = new Image()
const textImage = new Image()
finalCtx.imageSmoothingEnabled = true
finalCtx.imageSmoothingQuality = 'high'

lines.forEach((line, index) => {
let xPosition = margin
if (horizontalAlign) {
const textWidth = finalCtx.measureText(line).width
xPosition = (size - textWidth) / 2
}
finalCtx.fillText(
line,
xPosition,
baselineOffset + (index + 1) * lineHeight
)
})

return new Promise((resolve, reject) => {
textImage.src = textCtx.canvas.toDataURL('svg')
textImage.onload = () => {
scaledCtx.imageSmoothingEnabled = true
scaledCtx.canvas.width = size
scaledCtx.canvas.height = size
const hRatio = scaledCtx.canvas.width / textImage.width
const vRatio = scaledCtx.canvas.height / textImage.height
const ratio = Math.min(hRatio, vRatio)
const centerShift_x =
(scaledCtx.canvas.width - textImage.width * ratio) / 2
const centerShift_y =
(scaledCtx.canvas.height - textImage.height * ratio) / 2
scaledCtx.drawImage(
textImage,
0,
0,
textImage.width,
textImage.height,
centerShift_x,
centerShift_y,
textImage.width * ratio,
textImage.height * ratio
)
const scaledImage = scaledCtx.canvas.toDataURL('svg')
coverImage.src = scaledImage
coverImage.onload = async () => {
finalCtx.fillStyle = 'transparent'
finalCtx.fillRect(0, 0, size, size)
finalCtx.drawImage(
coverImage,
0,
0,
size,
size,
font_size,
font_size,
size - font_size,
size - font_size
)
finalCtx.canvas.toBlob((blob) => {
if (!blob) {
reject(new Error('Failed to generate image'))
return
}
const file = new File([blob], 'Generated Cover.png', {
type: 'image/png',
})
resolve(file)
}, 'image/png')
finalCtx.canvas.toBlob((blob) => {
if (!blob) {
reject(new Error('Failed to generate image'))
return
}
}
const file = new File([blob], 'Generated Cover.png', {
type: 'image/png',
})
resolve(file)
}, 'image/png')
})
}

Loading