diff --git a/index.html b/index.html index e2a8502..bb4b2ad 100644 --- a/index.html +++ b/index.html @@ -3,9 +3,21 @@ -vibegif +vibegif · <!--count--> free public domain gifs and animated webp - + + + + + + + + + + + + + + + +ghost float +

nothing here

+back to the gallery + + diff --git a/public/og.png b/public/og.png new file mode 100644 index 0000000..1e7011e Binary files /dev/null and b/public/og.png differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..caaf904 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://vibegif.direct-img.link/sitemap.xml diff --git a/vite.config.js b/vite.config.js index 0c89f3d..82ac522 100644 --- a/vite.config.js +++ b/vite.config.js @@ -4,13 +4,16 @@ // hand-made files that have no gen file go in the first section // only files inside a subject folder are tiles, so root files like favicon.webp stay out // tile images get ?v= so browsers drop old copies when a file changes, links stay clean for hotlinking +// gets CC0 license data for every tile image, so image search can list them under creative commons +// and the build writes sitemap.xml with the page and its tile images (google reads up to 1000 images per page) import{readdirSync as rd,readFileSync as rf}from'fs' import{createHash}from'crypto' +const site='https://vibegif.direct-img.link',cc0='https://creativecommons.org/publicdomain/zero/1.0/' const sections=['bw','color','anime','manga','mini','minicolor'] const hidden=['cat/tail.gif'] // same drawing as cat/wag.gif, kept so old links still work const walk=d=>rd(d,{withFileTypes:true}).flatMap(e=>e.isDirectory()?walk(`${d}/${e.name}`):`${d}/${e.name}`) const dims=(b,x)=>x=='gif'?[b.readUInt16LE(6),b.readUInt16LE(8)]:[b.readUIntLE(24,3)+1,b.readUIntLE(27,3)+1] -const gallery=()=>{ +const tiles=()=>{ const sec={},g=Object.fromEntries(sections.map(s=>[s,{}])) for(const f of walk('gen').filter(f=>f.endsWith('.js'))){const[,s,...r]=f.split('/');sec[r.join('/').slice(0,-3)]=s} for(const f of walk('public').filter(f=>/^public\/[^/]+\/.+\.(webp|gif)$/.test(f)).sort()){ @@ -18,12 +21,28 @@ const gallery=()=>{ if(!hidden.includes(p))(g[sec[id]??sections[0]][id]??={})[x]=f } return sections.map(s=>Object.entries(g[s]).map(([id,fm])=>{ - const x=fm.webp?'webp':'gif',b=rf(fm[x]),[w,h]=dims(b,x),v=createHash('md5').update(b).digest('hex').slice(0,8) - const links=['webp','gif'].filter(k=>fm[k]).map(k=>`${k}`).join(' ') - return`
${id.replace('/',' ')}
${links}
` - }).join('\n')).filter(Boolean).join('\n
\n') + const x=fm.webp?'webp':'gif',b=rf(fm[x]),[w,h]=dims(b,x) + return{id,fm,x,w,h,src:`/${id}.${x}?v=${createHash('md5').update(b).digest('hex').slice(0,8)}`} + })) } -export default{plugins:[{name:'gallery',transformIndexHtml:h=>{ - const g=gallery() - return h.replace('',g).replaceAll('',g.split('
').length-1) -}}]} +const figure=t=>{ + const links=['webp','gif'].filter(k=>t.fm[k]).map(k=>`${k}`).join(' ') + return`
${t.id.replace('/',' ')}
${links}
` +} +const ld=all=>`` +const sitemap=all=>` + +${site}/${new Date().toISOString().slice(0,10)} +${all.map(t=>`${site+t.src}`).join('\n')} + + +` +export default{plugins:[{name:'gallery', + transformIndexHtml:h=>{ + const s=tiles(),all=s.flat() + return h.replace('',s.map(ts=>ts.map(figure).join('\n')).filter(Boolean).join('\n
\n')) + .replace('',ld(all)).replaceAll('',all.length) + }, + generateBundle(){this.emitFile({type:'asset',fileName:'sitemap.xml',source:sitemap(tiles().flat())})} +}]}