cq

Distributed social media platform
git clone git://git.finwo.net/app/cq
Log | Files | Refs

commit 41df82ec75ea3849b76b6d13360d24904ca2e173
parent 80fb7f680dd97246273846599c9e333a6e9d3fdc
Author: finwo <finwo@pm.me>
Date:   Sun, 14 Sep 2025 22:47:52 +0200

Added icons

Diffstat:
M.github/workflows/build-and-deploy.yml | 1+
M.gitignore | 1+
Aicon.html | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackages/app/build.js | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dpackages/app/esbuild.js | 90-------------------------------------------------------------------------------
Mpackages/app/package.json | 2+-
Apackages/app/src/assets/icon.png | 0
Mpackages/app/src/assets/manifest.json | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mpackages/app/src/component/screen-account-create.tsx | 1+
9 files changed, 331 insertions(+), 92 deletions(-)

diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml @@ -16,6 +16,7 @@ jobs: - name: Install and Build run: | + sudo apt install -yq ffmpeg echo "BASEURL_APP='\"https://cq.finwo.net/#!\"'" > packages/app/.env npm install npm run build diff --git a/.gitignore b/.gitignore @@ -1 +1,2 @@ node_modules/ +.DS_Store diff --git a/icon.html b/icon.html @@ -0,0 +1,138 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"/> + </head> + <body> + <canvas id="org" width="1024" height="1024"></canvas> + <script> + (async () => { + let ctx = org.getContext('2d'); + let idata = ctx.getImageData(0, 0, org.width, org.height); + let center = []; + let radius = 128; + + for(let y=0; y<org.height; y++) { + for(let x=0; x<org.width; x++) { + const idx = ((y*org.width)+x)*4; + idata.data[idx+0] = 34; + idata.data[idx+1] = 34; + idata.data[idx+2] = 34; + idata.data[idx+3] = 255; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + + // octagon outer + center = [512,512]; + radius = 360; + for(let x=0; x<org.width; x++) { + for(let y=0; y<org.height; y++) { + const idx = ((y*org.width)+x)*4; + let xoff = x-center[0]; + let yoff = y-center[1]; + if (Math.abs(xoff) > radius) continue; + if (Math.abs(yoff) > radius) continue; + if (Math.abs(xoff)+Math.abs(yoff) > (radius*1.45)) continue; + idata.data[idx+0] = 255; + idata.data[idx+1] = 255; + idata.data[idx+2] = 255; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + + // octagon inner + center = [512,512]; + radius = 264; + for(let x=0; x<org.width; x++) { + for(let y=0; y<org.height; y++) { + const idx = ((y*org.width)+x)*4; + let xoff = x-center[0]; + let yoff = y-center[1]; + if (Math.abs(xoff) > radius) continue; + if (Math.abs(yoff) > radius) continue; + if (Math.abs(xoff)+Math.abs(yoff) > (radius*1.45)) continue; + idata.data[idx+0] = 34; + idata.data[idx+1] = 34; + idata.data[idx+2] = 34; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + + // Remove bottom right + for(let x=600; x<org.width; x++) { + for(let y=600; y<org.height; y++) { + const idx = ((y*org.width)+x)*4; + idata.data[idx+0] = 34; + idata.data[idx+1] = 34; + idata.data[idx+2] = 34; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + + // // Extend top bar + // for(let x=512; x<(512+312); x++) { + // for(let y=(512-360); y<(512-264); y++) { + // const idx = ((y*org.width)+x)*4; + // idata.data[idx+0] = 255; + // idata.data[idx+1] = 255; + // idata.data[idx+2] = 255; + // } + // } + // ctx.putImageData(idata, 0, 0); + // await new Promise(r => requestAnimationFrame(r)); + // for(let x=512; x<(512+324); x++) { + // for(let y=(512+264+1); y<(512+360+1); y++) { + // const idx = ((y*org.width)+x)*4; + // idata.data[idx+0] = 255; + // idata.data[idx+1] = 255; + // idata.data[idx+2] = 255; + // } + // } + // ctx.putImageData(idata, 0, 0); + // await new Promise(r => requestAnimationFrame(r)); + + // Diagonal, mixes Q into C + for(let x=512; x<(512+360+1); x++) { + for(let y=512; y<(512+360+1); y++) { + if (Math.abs(x-y) > (360-264)/1.41) continue; + const idx = ((y*org.width)+x)*4; + idata.data[idx+0] = 255; + idata.data[idx+1] = 255; + idata.data[idx+2] = 255; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + + // Stretch lower side + for(let y=1024; y>488; y--) { + for(let x=0; x<1024; x++) { + const idx = ((y*org.width)+x)*4; + idata.data[idx+0] = idata.data[idx-(org.width*128)+0]; + idata.data[idx+1] = idata.data[idx-(org.width*128)+1]; + idata.data[idx+2] = idata.data[idx-(org.width*128)+2]; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + + // Stretch upper side + for(let y=0; y<488; y++) { + for(let x=0; x<1024; x++) { + const idx = ((y*org.width)+x)*4; + idata.data[idx+0] = idata.data[idx+(org.width*128)+0]; + idata.data[idx+1] = idata.data[idx+(org.width*128)+1]; + idata.data[idx+2] = idata.data[idx+(org.width*128)+2]; + } + } + ctx.putImageData(idata, 0, 0); + await new Promise(r => requestAnimationFrame(r)); + })(); + </script> + </body> +</html> diff --git a/packages/app/build.js b/packages/app/build.js @@ -0,0 +1,112 @@ +#!/usr/bin/env node + +require('dotenv').config(); +const cpy = require('cpy').default; +const fs = require('node:fs'); +const { dirname } = require('node:path'); +const { fileURLToPath } = require('node:url'); +const { exec } = require('node:child_process'); +const esbuild = require('esbuild'); +const { nodeModulesPolyfillPlugin } = require('esbuild-plugins-node-modules-polyfill'); + +// const __dirname = dirname(fileURLToPath(import.meta.url)); + +const entryPoints = { + 'main': __dirname + '/src/main.ts', +}; + +const define = { + 'process.env.BASEURL_APP': process.env.BASEURL_APP || '"http://localhost:4000/#!"', +}; + +const config = { + format: 'esm', + platform: 'browser', + target: ['chrome108','firefox107'], + mainFields: ['browser','module','main'], + bundle: true, + outdir: __dirname + '/dist', + entryPoints: Object.values(entryPoints), + minify: false, + define, + + jsxFactory : 'm', + jsxFragment : '"["', + + loader: { + '.eot' : 'dataurl', + '.html' : 'text', + '.wasm' : 'dataurl', + '.png' : 'dataurl', + '.svg' : 'dataurl', + '.ttf' : 'dataurl', + '.woff' : 'dataurl', + '.woff2': 'dataurl', + }, + + plugins: [ + nodeModulesPolyfillPlugin({ + globals: { + Buffer: true + } + }) + ], + +}; + +const buildList = []; +const styles = ['global.css']; + +esbuild + .build(config) + .then(async () => { + try { fs.mkdirSync('./dist/assets'); } catch { /* empty */ } + const r = await cpy(__dirname + '/src/assets/*', __dirname + '/dist/assets'); +// try { fs.mkdirSync('./dist/AppModule'); } catch { /* empty */ } + fs.copyFileSync(`./src/global.css`, `./dist/global.css`); + for(const name of Object.keys(entryPoints)) { + buildList.push(`${name}.js`); + try { + fs.statSync(config.outdir + `/${name}.css`); + styles.push(`${name}.css`); + } catch(e) { + // Intentionally empty + } + } + + fs.writeFileSync(config.outdir + `/index.html`, `<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <link rel="icon" href="/assets/favicon.ico"> + <link rel="apple-touch-icon" href="/assets/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="192x192" href="/assets/icon-192x192.png"> + <link rel="icon" type="image/png" sizes="512x512" href="/assets/icon-512x512.png"> + <link rel="manifest" href="/assets/manifest.json"/> + ${styles.map(name => `<link rel="preload" as="style" href="${name}" onload="this.onload=null;this.rel='stylesheet'"/>`).join('\n ')} + </head> + <body> + ${buildList.map(name => `<script type="module" src="${name}" defer></script>`).join('\n ')} + </body> +</html> +`); + + // Build favicons + await new Promise(done => { + exec(`ffmpeg -n -i ${config.outdir}/assets/icon.png -vf scale=32:32 -sws_flags area ${config.outdir}/assets/favicon.ico`, done); + }); + await new Promise(done => { + exec(`ffmpeg -n -i ${config.outdir}/assets/icon.png -vf scale=180:180 -sws_flags area ${config.outdir}/assets/apple-touch-icon.png`, done); + }); + + // Build app icons + const manifest = require(__dirname+'/dist/assets/manifest.json'); + for(const format of manifest.icons) { + const scale = format.sizes.split('x').join(':'); + await new Promise(done => { + exec(`ffmpeg -n -i ${config.outdir}/assets/icon.png -vf scale=${scale} -sws_flags area ${config.outdir}${format.src}`, done); + }); + } + + }) diff --git a/packages/app/esbuild.js b/packages/app/esbuild.js @@ -1,90 +0,0 @@ -#!/usr/bin/env node - -require('dotenv').config(); -const cpy = require('cpy').default; -const fs = require('node:fs'); -const { dirname } = require('node:path'); -const { fileURLToPath } = require('node:url'); -const esbuild = require('esbuild'); -const { nodeModulesPolyfillPlugin } = require('esbuild-plugins-node-modules-polyfill'); - -// const __dirname = dirname(fileURLToPath(import.meta.url)); - -const entryPoints = { - 'main': __dirname + '/src/main.ts', -}; - -const define = { - 'process.env.BASEURL_APP': process.env.BASEURL_APP || '"http://localhost:4000/#!"', -}; - -const config = { - format: 'esm', - platform: 'browser', - target: ['chrome108','firefox107'], - mainFields: ['browser','module','main'], - bundle: true, - outdir: __dirname + '/dist', - entryPoints: Object.values(entryPoints), - minify: false, - define, - - jsxFactory : 'm', - jsxFragment : '"["', - - loader: { - '.eot' : 'dataurl', - '.html' : 'text', - '.wasm' : 'dataurl', - '.png' : 'dataurl', - '.svg' : 'dataurl', - '.ttf' : 'dataurl', - '.woff' : 'dataurl', - '.woff2': 'dataurl', - }, - - plugins: [ - nodeModulesPolyfillPlugin({ - globals: { - Buffer: true - } - }) - ], - -}; - -const buildList = []; -const styles = ['global.css']; - -esbuild - .build(config) - .then(async () => { - try { fs.mkdirSync('./dist/assets'); } catch { /* empty */ } - const r = await cpy(__dirname + '/src/assets/*', __dirname + '/dist/assets'); -// try { fs.mkdirSync('./dist/AppModule'); } catch { /* empty */ } - fs.copyFileSync(`./src/global.css`, `./dist/global.css`); - for(const name of Object.keys(entryPoints)) { - buildList.push(`${name}.js`); - try { - fs.statSync(config.outdir + `/${name}.css`); - styles.push(`${name}.css`); - } catch(e) { - // Intentionally empty - } - } - - fs.writeFileSync(config.outdir + `/index.html`, `<!DOCTYPE html> -<html> - <head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width,initial-scale=1"> - <link rel="manifest" href="/assets/manifest.json"/> - ${styles.map(name => `<link rel="preload" as="style" href="${name}" onload="this.onload=null;this.rel='stylesheet'"/>`).join('\n ')} - </head> - <body> - ${buildList.map(name => `<script type="module" src="${name}" defer></script>`).join('\n ')} - </body> -</html> -`); - - }) diff --git a/packages/app/package.json b/packages/app/package.json @@ -7,7 +7,7 @@ "watch": "npx -y nodemon -w src -e css,ts,tsx --exec npm -- run build", "serve": "PORT=4000 npx -y serve dist", "dev": "npx -y concurrently 'npm run watch' 'npm run serve'", - "build": "node esbuild.js" + "build": "node build.js" }, "keywords": [], "author": "", diff --git a/packages/app/src/assets/icon.png b/packages/app/src/assets/icon.png Binary files differ. diff --git a/packages/app/src/assets/manifest.json b/packages/app/src/assets/manifest.json @@ -1,7 +1,83 @@ { "short_name": "CQ", "name": "Seek You", - "icons": [], + "icons": [ + { + "src": "/assets/icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "/assets/icon-384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "/assets/icon-256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "/assets/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/assets/icon-180.png", + "sizes": "180x180", + "type": "image/png" + }, + { + "src": "/assets/icon-152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "/assets/icon-144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "/assets/icon-120.png", + "sizes": "120x120", + "type": "image/png" + }, + { + "src": "/assets/icon-114.png", + "sizes": "114x114", + "type": "image/png" + }, + { + "src": "/assets/icon-96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "/assets/icon-76.png", + "sizes": "76x76", + "type": "image/png" + }, + { + "src": "/assets/icon-72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "/assets/icon-60.png", + "sizes": "60x60", + "type": "image/png" + }, + { + "src": "/assets/icon-57.png", + "sizes": "57x57", + "type": "image/png" + }, + { + "src": "/assets/icon-48.png", + "sizes": "48x48", + "type": "image/png" + } + ], "start_url": "/", "display": "standalone", "theme_color": "black", diff --git a/packages/app/src/component/screen-account-create.tsx b/packages/app/src/component/screen-account-create.tsx @@ -46,6 +46,7 @@ export const AccountCreateScreen = { if (step === 4) { await new Promise(r => setTimeout(r, 10)); const outputEl = document.getElementById('buildOutput') || document.createElement('div'); + outputEl.innerText += '\n'; // // Store seed (TODO: don't) // const accountSeedHandle = await getFileHandle(`/local/accounts/${vnode.state.accountId}/seed.txt`, { create: true })