{entry.title}
{contributor.displayName}
{formatAddress(contributor.address)}{contributor.displayName}
{formatAddress(contributor.address)}Internal Error
Return Home{publication.ens}
Page Not Found
{contributor.displayName}
{contributor.address.substr(0, 6)}Patience
The NFT embed failed to load
{data!.title}
{data!.description}
{new URL(data!.url!).hostname}
[ match.groups!.url, await calculateImageSize(match.groups!.url), ]) ) ) } export const calculateImageSize = async (url: string): Promise=> { const image = await fetch(url).then(res => res.arrayBuffer()) return sizeOf(new Uint8Array(image)) } ================================================ FILE: src/utils/markdown.tsx ================================================ 'use client' import Embed from 'react-embed' import NextImage from 'next/image' import NFT from '@/components/NFT' import { shouldEmbed } from './embeds' import getConfig from '@/hooks/getConfig' import { useTheme } from '@/context/theme' import Zoom from 'react-medium-image-zoom' import OpenGraph from '@/components/OpenGraph' import EntryLink from '@/components/EntryLink' import { useImageSizes } from '@/context/image_sizes' export const Image = ({ alt, src }) => { const { colorMode } = useTheme() const { [src]: { width, height }, } = useImageSizes() return ( ) } export const LinkOrEmbed = ({ href, children, ...props }) => { const { colorMode } = useTheme() const { ensDomain } = getConfig() const blockSize = props?.node?.blockSize if (blockSize != 1) return {alt && {alt} }{children} const parsedURL = new URL(href) if (parsedURL.protocol === 'nft:') { const [chainId, contract, tokenId] = parsedURL.pathname.replace(/^\/+/, '').split('/') return} if (parsedURL.protocol === 'crowdfund:') { const [, crowdfundAddress] = href.match(/crowdfund:\/\/(\w*)/m) return {children} } if (typeof window !== 'undefined' && shouldEmbed(href)) { return } return{children} } const getClass = (accentColor: string): string => { switch (accentColor.toLowerCase()) { case 'purple': return '!border-fuchsia-400' case 'pink': return '!border-red-500' case 'red': return '!border-red-500' case 'orange': return '!border-orange-400' case 'yellow': return '!border-yellow-400' case 'teal': return '!border-cyan-400' case 'blue': return '!border-blue-500' case 'indigo': return '!border-indigo-400' case 'green': return '!border-emerald-400' case 'foreground': return '!border-white' default: return '!border-blue-400' } } export const BlockQuote = ({ children }) => { const { accent } = useTheme() return{children}} export const Block = ({ children }) => { children = Array.isArray(children) ? children : [children] const blockAwareChildren = children.map(child => { if (child?.props?.node) child.props.node.blockSize = children.length return child }) return{blockAwareChildren}
} ================================================ FILE: src/utils/url.ts ================================================ const allowedLinkProtocols = ['http', 'https', 'mailto', 'tel', 'crowdfund', 'nft'] export const uriTransformer = (uri: string): string => { const url = (uri || '').trim() const first = url.charAt(0) if (first === '#' || first === '/') { return url } const colon = url.indexOf(':') if (colon === -1) { return url } const length = allowedLinkProtocols.length let index = -1 while (++index < length) { const protocol = allowedLinkProtocols[index] if (colon === protocol.length && url.slice(0, protocol.length).toLowerCase() === protocol) { return url } } index = url.indexOf('?') if (index !== -1 && colon > index) { return url } index = url.indexOf('#') if (index !== -1 && colon > index) { return url } return '#' } ================================================ FILE: tailwind.config.js ================================================ import colors from 'tailwindcss/colors' import defaultTheme from 'tailwindcss/defaultTheme' export default { content: ['./src/**/*.{js,ts,jsx,tsx}'], darkMode: 'class', theme: { extend: { colors: { gray: colors.neutral, fuchsia: colors.fuchsia, orange: colors.orange, cyan: colors.cyan, emerald: colors.emerald, }, fontFamily: { sans: ['var(--font-inter)', ...defaultTheme.fontFamily.sans], mono: ['iAWriter Mono', ...defaultTheme.fontFamily.mono], }, boxShadow: { card: '0 0 0.5rem rgba(0, 0, 0, 0.075)', }, typography: theme => ({ DEFAULT: { css: { whiteSpace: 'pre-wrap', '> *, blockquote > *': { marginTop: '0 !important', marginBottom: '0 !important', }, figcaption: { textAlign: 'center', }, strong: { fontWeight: theme('fontWeight.medium'), }, img: { marginTop: null, marginBottom: null, borderRadius: theme('borderRadius.lg'), }, blockquote: { display: 'flex', fontStyle: null, borderLeftWidth: '2px', borderColor: theme('colors.blue.600'), fontWeight: theme('fontWeight.normal'), }, hr: { borderTopWidth: '2px', maxWidth: '12rem', marginLeft: 'auto', marginRight: 'auto', }, 'blockquote p:first-of-type::before, blockquote p:last-of-type::after': { content: 'unset !important', }, a: { color: theme('colors.blue.600'), wordWrap: 'break-word', fontWeight: theme('fontWeight.normal'), textDecoration: 'none', textUnderlineOffset: '0.2em', '&:hover': { textDecoration: 'underline', }, }, 'ol > li::before': { fontFamily: theme('fontFamily.mono').join(', '), color: theme('colors.gray.400'), }, 'ul > li::before': { backgroundColor: theme('colors.gray.400'), }, '.twitter-tweet': { marginLeft: 'auto', marginRight: 'auto', }, code: { fontWeight: theme('fontWeight.normal'), background: theme('colors.gray.200'), color: theme('colors.gray.600'), borderRadius: theme('borderRadius.lg'), padding: `0.125rem ${theme('padding.1')}`, '&::before, &::after': { content: 'unset !important', }, }, '[data-nft] *, .opengraph *, .opengraph *:hover': { margin: '0', textDecoration: 'none', borderRadius: 'unset', }, }, }, dark: { css: { color: theme('colors.gray.300'), strong: { color: theme('colors.gray.300'), }, 'h1, h2, h3, h4, h5, h6': { color: theme('colors.gray.200'), }, blockquote: { color: theme('colors.gray.300'), borderColor: theme('colors.yellow.400'), }, 'ol > li::before': { color: theme('colors.gray.500'), }, 'ul > li::before': { backgroundColor: theme('colors.gray.600'), }, ':not(pre) > code': { background: theme('colors.gray.800'), color: 'unset', }, hr: { borderColor: theme('colors.gray.700'), opacity: '0.6', }, }, }, lg: { css: { 'ol > li, ul > li': { marginTop: '0', marginBottom: '0', }, // Image margin is handled by `figure` img: { marginTop: null, marginBottom: null, }, }, }, }), }, }, plugins: [require('@tailwindcss/typography')], } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": false, "noEmit": true, "incremental": true, "module": "ESNext", "target": "ESNext", "esModuleInterop": true, "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "baseUrl": ".", "paths": { "@/*": ["src/*"] }, "plugins": [ { "name": "next" } ], "strictNullChecks": true }, "include": [ "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/scripts/resolve-ens.js", "next.config.js" ], "exclude": ["node_modules"] }