feat: manually generate ogi to speed up builds

This commit is contained in:
Cory Dransfeldt 2024-05-02 14:49:04 -07:00
parent 25cc2f9d77
commit 7b7ce22aad
No known key found for this signature in database
303 changed files with 87 additions and 114 deletions
.eleventy.js
config
events
filters
package-lock.jsonpackage.json
scripts/og-images
src
_data/json
_includes
assets/img/ogi
2021-reading-list-preview.png2022-reading-list-preview.pnga-brief-intro-to-git-preview.pnga-cadillac-for-your-thoughts-2015-in-music-preview.pnga-music-workflow-for-2024-preview.pnga-retrospective-on-a-year-without-streaming-music-preview.pnga-safari-specific-guide-to-making-the-modern-web-suck-less-preview.pnga-vote-for-pai-is-a-vote-against-consumers-and-for-big-cable-preview.pngaccess-to-data-isnt-a-grant-to-exploit-it-preview.pngadding-a-light-dark-theme-toggle-preview.pngadding-client-side-rendered-webmentions-to-my-blog-preview.pngadding-client-side-webmentions-to-my-nextjs-blog-preview.pngai-cannot-and-should-not-replace-search-preview.pngajit-pai-accused-of-conflict-for-helping-former-client-preview.pngajit-pai-follows-congress-instructions-requires-new-anti-robocall-tech-preview.pngamericas-internet-freedom-rating-drops-following-net-neutrality-repeal-preview.pngan-indie-web-primer-preview.pnganother-eleventy-content-syndication-path-preview.pngapple-centric-digital-privacy-tools-preview.pngapple-messages-a-tale-of-woe-or-how-to-fix-sync-a-crash-loop-and-accept-data-loss-preview.pngapple-music-a-tale-of-woe-preview.pngarcade-fire-reflektor-preview.pngaustralian-political-parties-hacked-preview.pngautomate-and-syndicate-content-from-eleventy-to-mastodon-preview.pngautomatic-feedbin-subscription-backups-preview.pngautomatic-mastodon-post-embeds-preview.pngautomating-and-probably-overengineering-my-now-page-preview.pngautomating-email-cleanup-in-gmail-preview.pngautomating-package-tracking-on-ios-preview.pngautomating-rss-syndication-and-sharing-with-nextjs-and-github-preview.pngavoiding-phishing-preview.pngbackdoor-password-in-junipers-firewall-code-preview.pngben-thompson-on-net-neutrality-preview.pngbill-promises-californians-more-control-over-their-data-preview.pngblitzen-trapper-ever-loved-once-preview.pngblocking-spam-calls-from-similar-numbers-on-ios-preview.pngbombino-and-hanni-el-khatib-at-the-santa-monica-pier-preview.pngbrowsing-the-mobile-web-sucks-preview.pngbuilding-a-bespoke-now-playing-web-component-preview.pngbuilding-a-now-page-using-nextjs-and-social-apis-preview.pngbuilding-a-popular-posts-widget-in-eleventy-using-plausible-analytics-preview.pngbuilding-a-reactive-website-preview.pngbuilding-a-scrobbler-using-plex-webhooks-edge-functions-and-blob-storage-preview.pngbuilding-a-theme-toggle-web-component-preview.pngbuilding-my-now-page-using-eleventy-preview.pngca-governor-signs-nations-strictest-net-neutrality-law-preview.pngca-net-neutrality-bill-back-on-track-preview.pngca-net-neutrality-bill-makes-a-comeback-preview.pngca-senate-passes-strict-net-neutrality-law-in-defiance-of-isps-preview.pngcalifornia-approves-new-online-privacy-rules-preview.pngcalifornia-passes-net-neutrality-bill-preview.pngcalifornia-state-senate-passes-net-neutrality-legislation-preview.pngcastro-v23-released-preview.pngcbo-analysis-confirms-gop-health-bill-is-little-more-than-class-warfare-preview.pngchanges-coming-to-droplr-preview.pngcheck-if-images-are-available-before-optimizing-in-eleventy-preview.pngcheck-in-to-your-personal-site-preview.pngchrome-tool-helps-developers-make-websites-more-color-blind-friendly-preview.pngclearing-modpagespeed-cache-preview.pngcomcast-continues-to-whine-about-net-neutrality-preview.pngcomcast-throttling-mobile-video-and-charging-extra-for-high-quality-streaming-preview.pngcongress-guts-internet-privacy-protections-preview.pngconsolidation-swiftly-follows-the-death-of-net-neutrality-preview.pngcurrently-reading-preview.pngdamien-jurado-2014-preview.pngdata-collection-should-always-be-opt-in-preview.pngdata-is-a-toxic-asset-preview.pngdata-ownership-and-agency-preview.pngdawes-most-people-preview.pngdebugging-javascript-interview-with-mehdi-osman-preview.pngdeploying-a-jekyll-site-to-netlify-with-docker-and-gitlab-ci-preview.pngdesign-by-numbers-typography-preview.pngdhs-boss-calls-for-more-fear-less-encryption-preview.pngdigital-privacy-tools-preview.pngdisplaying-listening-data-from-apple-music-using-musickitjs-preview.pngdisplaying-now-playing-data-with-matching-emoji-using-netlify-edge-functions-and-eleventy-preview.pngdoj-takes-war-on-encryption-to-whatsapp-preview.pngdomain-names-as-discoverable-personal-identifiers-for-the-web-preview.pngdont-be-afraid-to-admit-when-you-dont-know-something-preview.pngdont-like-systematic-privacy-violations-stop-using-the-internet-preview.pngdont-pin-your-political-hopes-on-tech-giants-preview.pngdoppler-locally-stored-music-and-storage-as-a-beneficial-constraint-preview.pngdressing-for-the-surveillance-age-preview.pngdrying-up-now-page-templates-and-normalizing-data-in-eleventy-preview.pngduckduckgo-is-good-enough-for-regular-use-preview.pngdumb-pipes-preview.pngdutch-government-on-encryption-preview.pngearn-it-act-threatens-end-to-end-encryption-preview.pngearn-it-is-an-attack-on-encryption-preview.pngeff-argues-border-agents-need-warrants-to-search-digital-devices-preview.pngeffs-recommendations-for-consumer-data-privacy-laws-preview.png

View file

@ -10,7 +10,6 @@ import htmlmin from 'html-minifier-terser'
import filters from './config/filters/index.js'
import { slugifyString } from './config/utils/index.js'
import { svgToJpeg } from './config/events/index.js'
import { minifyJsComponents } from './config/events/index.js'
import { searchIndex, tagList, postStats, tagsSortedByCount, links, tagMap, booksToRead } from './config/collections/index.js'
@ -142,7 +141,6 @@ export default async function (eleventyConfig) {
})
// events
eleventyConfig.on('afterBuild', svgToJpeg)
eleventyConfig.on('afterBuild', minifyJsComponents)
return {

View file

@ -1,30 +1,6 @@
import fs from 'fs'
import Image from '@11ty/eleventy-img'
import { minify } from 'terser'
export const svgToJpeg = () => {
const socialPreviewImagesDir = '_site/assets/img/social-preview/'
fs.readdir(socialPreviewImagesDir, (err, files) => {
if (!!files && files.length > 0) {
files.forEach((fileName) => {
if (fileName.endsWith('.svg')) {
let imageUrl = socialPreviewImagesDir + fileName
Image(imageUrl, {
formats: ['jpeg'],
outputDir: './' + socialPreviewImagesDir,
filenameFormat: function (id, src, width, format) {
let outputFileName = fileName.substring(0, fileName.length - 4)
return `${outputFileName}.${format}`
},
})
}
})
} else {
console.log('⚠ No social images found')
}
})
}
export const minifyJsComponents = async () => {
const jsComponentsDir = '_site/assets/scripts/components';
const files = fs.readdirSync(jsComponentsDir);

View file

@ -22,17 +22,6 @@ export default {
const replacement = '&'
return string.replace(pattern, replacement)
},
splitLines: (input, maxCharLength) => {
const parts = input.split(' ')
const lines = parts.reduce(function (acc, cur) {
if (!acc.length) return [cur]
let lastOne = acc[acc.length - 1]
if (lastOne.length + cur.length > maxCharLength) return [...acc, cur]
acc[acc.length - 1] = lastOne + ' ' + cur
return acc
}, [])
return lines
},
stripUtm: (string) => {
if (!string) return
return string.replace(utmPattern, '')

67
package-lock.json generated
View file

@ -22,7 +22,6 @@
"@11ty/eleventy": "3.0.0-alpha.10",
"@11ty/eleventy-activity-feed": "^1.0.9",
"@11ty/eleventy-fetch": "^4.0.1",
"@11ty/eleventy-img": "^4.0.2",
"@11ty/eleventy-plugin-rss": "^1.2.0",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
"@11tyrocks/eleventy-plugin-lightningcss": "^1.4.0",
@ -31,6 +30,7 @@
"@remy/webmention": "^1.5.0",
"@rknightuk/eleventy-plugin-post-graph": "^1.0.6",
"dotenv-flow": "^4.1.0",
"gray-matter": "^4.0.3",
"html-minifier-terser": "^7.2.0",
"ics-to-json-extended": "^1.1.4",
"liquidjs": "^10.10.0",
@ -39,6 +39,7 @@
"markdown-it-anchor": "^8.4.1",
"markdown-it-footnote": "^4.0.0",
"sanitize-html": "^2.13.0",
"sharp": "^0.33.3",
"slugify": "^1.6.6",
"terser": "^5.30.1",
"writing-stats": "^1.0.6"
@ -211,28 +212,6 @@
"url": "https://opencollective.com/11ty"
}
},
"node_modules/@11ty/eleventy-img": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@11ty/eleventy-img/-/eleventy-img-4.0.2.tgz",
"integrity": "sha512-MSCkZRJk9rWa7nojx9HBMZJePOrm+V3XNpT091qguj61SG5UsgXbxAkoeejO3npmKIQJTyVIV/rrA6d7xZYOvw==",
"dev": true,
"dependencies": {
"@11ty/eleventy-fetch": "^4.0.0",
"brotli-size": "^4.0.0",
"debug": "^4.3.4",
"entities": "^4.5.0",
"image-size": "^1.1.1",
"p-queue": "^6.6.2",
"sharp": "^0.33.2"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/11ty"
}
},
"node_modules/@11ty/eleventy-plugin-bundle": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-2.0.2.tgz",
@ -1683,18 +1662,6 @@
"node": ">=8"
}
},
"node_modules/brotli-size": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz",
"integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==",
"dev": true,
"dependencies": {
"duplexer": "0.1.1"
},
"engines": {
"node": ">= 10.16.0"
}
},
"node_modules/browserslist": {
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
@ -2327,12 +2294,6 @@
"node": ">= 12.0.0"
}
},
"node_modules/duplexer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==",
"dev": true
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -3165,21 +3126,6 @@
"moment": "^2.29.1"
}
},
"node_modules/image-size": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz",
"integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==",
"dev": true,
"dependencies": {
"queue": "6.0.2"
},
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=16.x"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -4960,15 +4906,6 @@
"node": ">=6"
}
},
"node_modules/queue": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
"dev": true,
"dependencies": {
"inherits": "~2.0.3"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",

View file

@ -1,6 +1,6 @@
{
"name": "coryd.dev",
"version": "13.4.9",
"version": "13.5.0",
"description": "The source for my personal site. Built using 11ty.",
"type": "module",
"scripts": {
@ -9,6 +9,7 @@
"start:quick": "eleventy --serve --incremental --ignore-initial",
"build": "ELEVENTY_PRODUCTION=true eleventy",
"debug": "DEBUG=Eleventy* npx @11ty/eleventy --serve",
"build:ogi": "node ./scripts/og-images/index.js",
"postbuild": "webmention _site/feeds/posts --limit 1 --send && webmention _site/feeds/links --limit 1 --send"
},
"keywords": [
@ -33,7 +34,6 @@
"@11ty/eleventy": "3.0.0-alpha.10",
"@11ty/eleventy-activity-feed": "^1.0.9",
"@11ty/eleventy-fetch": "^4.0.1",
"@11ty/eleventy-img": "^4.0.2",
"@11ty/eleventy-plugin-rss": "^1.2.0",
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
"@11tyrocks/eleventy-plugin-lightningcss": "^1.4.0",
@ -42,6 +42,7 @@
"@remy/webmention": "^1.5.0",
"@rknightuk/eleventy-plugin-post-graph": "^1.0.6",
"dotenv-flow": "^4.1.0",
"gray-matter": "^4.0.3",
"html-minifier-terser": "^7.2.0",
"ics-to-json-extended": "^1.1.4",
"liquidjs": "^10.10.0",
@ -50,6 +51,7 @@
"markdown-it-anchor": "^8.4.1",
"markdown-it-footnote": "^4.0.0",
"sanitize-html": "^2.13.0",
"sharp": "^0.33.3",
"slugify": "^1.6.6",
"terser": "^5.30.1",
"writing-stats": "^1.0.6"

View file

@ -0,0 +1,74 @@
import { promises as fs } from 'fs'
import path from 'path'
import sharp from 'sharp'
import matter from 'gray-matter'
import slugify from 'slugify'
import { Liquid } from 'liquidjs'
import { DateTime } from 'luxon'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const baseDir = path.join(__dirname, '../../src/posts')
const outputDir = path.join(__dirname, '../../src/assets/img/ogi/')
const engine = new Liquid({ extname: '.liquid' })
engine.registerFilter('date', (isoDateString, formatString = 'MMMM d, yyyy') => {
const date = DateTime.fromISO(isoDateString)
return date.isValid ? date.toFormat(formatString) : isoDateString
})
engine.registerFilter('splitLines', (input, maxCharLength) => {
return input.split(' ').reduce((acc, cur) => {
if (!acc.length || acc[acc.length - 1].length + cur.length + 1 > maxCharLength) {
acc.push(cur)
} else {
acc[acc.length - 1] += ' ' + cur
}
return acc
}, [])
})
engine.registerFilter('slugify', (input) => slugify(input, { lower: true, strict: true, remove: /[*+~.()'"!:@]/g }))
const generateSVGAndConvertToPNG = async (filePath) => {
try {
const fileContent = await fs.readFile(filePath, 'utf8')
const { data } = matter(fileContent)
const svgTemplatePath = path.resolve(__dirname, 'index.liquid')
const templateContent = await fs.readFile(svgTemplatePath, 'utf8')
const svgContent = await engine.parseAndRender(templateContent, { preview: { data: data, date: data.date }})
const outputFile = path.join(outputDir, `${engine.filters.slugify(data.title)}-preview.png`)
await fs.mkdir(outputDir, { recursive: true })
await sharp(Buffer.from(svgContent)).png().toFile(outputFile)
console.log(`Generated png at ${outputFile}`)
} catch (error) {
console.error('Error processing file:', error)
}
}
const processYearDirectories = async (baseDir) => {
try {
const yearDirs = await fs.readdir(baseDir, { withFileTypes: true })
for (const dirent of yearDirs) {
if (dirent.isDirectory()) {
const yearPath = path.join(baseDir, dirent.name)
const markdownFiles = await fs.readdir(yearPath, { withFileTypes: true })
for (const file of markdownFiles) {
if (file.isFile() && file.name.endsWith('.md')) {
const filePath = path.join(yearPath, file.name)
await generateSVGAndConvertToPNG(filePath)
}
}
}
}
} catch (error) {
console.error('Failed to process directories:', error)
}
}
const generateOgImages = async () => await processYearDirectories(baseDir)
generateOgImages()

View file

@ -1,11 +1,3 @@
---
pagination:
data: collections.posts
size: 1
alias: preview
permalink: '/assets/img/social-preview/{{ preview.data.title | slugify }}-preview.svg'
eleventyExcludeFromCollections: true
---
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1200" height="630" viewBox="0 0 1200 630" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
{% assign titleInLines = preview.data.title | splitLines: 40 %}
@ -32,7 +24,7 @@ eleventyExcludeFromCollections: true
fill="#fff"
>
<tspan x="80" y="{{ verticalStartingPoint | minus: 120 }}">
{{ preview.date | date: "%B %e, %Y" }}
{{ preview.data.date | date: "MMMM d, yyyy" }}
</tspan>
</text>

Before

(image error) Size: 1.9 KiB

After

(image error) Size: 1.7 KiB

View file

@ -1684,6 +1684,11 @@
"genre": "death metal",
"image": "https://coryd.dev/media/artists/unaussprechlichen-kulten.jpg"
},
"undergang": {
"mbid": "6853b4ec-8cd4-46a4-b970-8331d4587c43",
"genre": "death metal",
"image": "https://coryd.dev/media/artists/undergang.jpg"
},
"under-the-church": {
"mbid": "a2bdbefc-2cfd-43a2-8728-6352f038628a",
"genre": "death metal",

View file

@ -25,7 +25,7 @@
<meta property="og:url" content="{{ fullUrl }}" />
<meta
property="og:image"
content="{%- if schema == 'blog' %}{{ meta.url }}/assets/img/social-preview/{{ title | slugify }}-preview.jpeg{%- else -%}{{ meta.meta_data.opengraph_default }}{% endif -%}"
content="{%- if schema == 'blog' %}{{ meta.url }}/assets/img/ogi/{{ title | slugify }}-preview.png{%- else -%}{{ meta.meta_data.opengraph_default }}{% endif -%}"
/>
<meta name="theme-color" content="{{ meta.themeColor }}" />
<meta name="generator" content="Eleventy">

Binary file not shown.

After

(image error) Size: 24 KiB

Binary file not shown.

After

(image error) Size: 24 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 36 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 33 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 24 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 35 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 36 KiB

Binary file not shown.

After

(image error) Size: 25 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 33 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 33 KiB

Binary file not shown.

After

(image error) Size: 33 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 35 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 35 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 35 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 33 KiB

Binary file not shown.

After

(image error) Size: 24 KiB

Binary file not shown.

After

(image error) Size: 25 KiB

Binary file not shown.

After

(image error) Size: 29 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 25 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 25 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 35 KiB

Binary file not shown.

After

(image error) Size: 33 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 36 KiB

Binary file not shown.

After

(image error) Size: 31 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Some files were not shown because too many files have changed in this diff Show more