Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
This commit is contained in:
817 files changed
+143101
No files matched your search
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
# Each app: macOS squircle, shapeless Linux and Windows icons.
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
icons=$root/assets/icons
|
||||
work=$(mktemp -d)
|
||||
trap 'rm -rf "$work"' EXIT
|
||||
fail() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$(uname)" = Darwin ] || {
|
||||
echo "SKIP icons: macOS only (AppKit renders the masters)"
|
||||
exit 0
|
||||
}
|
||||
python3 -c 'import PIL' 2>/dev/null || {
|
||||
echo "SKIP icons: no Pillow"
|
||||
exit 0
|
||||
}
|
||||
swiftc -O -o "$work/icon" "$root/packaging/icons/icon.swift"
|
||||
# The master build.sh uses at `px`; small cut below 48.
|
||||
master() { # slug, variant, px
|
||||
local small=$icons/$1-small$2.svg
|
||||
if [ "$3" -lt 48 ] && [ -f "$small" ]; then echo "$small"; else echo "$icons/$1$2.svg"; fi
|
||||
}
|
||||
count=0
|
||||
while IFS=$'\t' read -r slug _; do
|
||||
for file in "$slug.svg" "$slug-macos.svg" "$slug.icns" "$slug.png" "$slug.ico"; do
|
||||
[ -s "$icons/$file" ] || fail "no $icons/$file; run packaging/icons/build.sh"
|
||||
done
|
||||
for made in icns png ico; do
|
||||
for source in "$icons/$slug".svg "$icons/$slug"-*.svg; do
|
||||
[ "$source" -nt "$icons/$slug.$made" ] && fail "$slug.$made is older than $(basename "$source"); run packaging/icons/build.sh"
|
||||
done
|
||||
done
|
||||
# Every shipped size, rendered afresh from its master.
|
||||
mkdir "$work/$slug"
|
||||
for px in 16 24 32 48 64 128 256 512 1024; do
|
||||
"$work/icon" "$px" "$(master "$slug" "" "$px")" "$work/$slug/plain-$px.png"
|
||||
"$work/icon" "$px" "$(master "$slug" -macos "$px")" "$work/$slug/macos-$px.png"
|
||||
done
|
||||
iconutil -c iconset -o "$work/$slug.iconset" "$icons/$slug.icns"
|
||||
python3 - "$icons" "$slug" "$work" <<'EOF' || fail "$slug: the rendered icons are wrong"
|
||||
import os, sys
|
||||
from PIL import Image
|
||||
icons, slug, work = sys.argv[1:]
|
||||
def raw(image):
|
||||
# Premultiplied: colour under zero alpha is not drawn.
|
||||
data = image.convert("RGBA").tobytes()
|
||||
out = bytearray(data)
|
||||
for i in range(0, len(data), 4):
|
||||
for c in range(3):
|
||||
out[i + c] = data[i + c] * data[i + 3] // 255
|
||||
return out
|
||||
def same(made, px, variant, what, most=1.0):
|
||||
want = raw(Image.open(f"{work}/{slug}/{variant}-{px}.png"))
|
||||
got = raw(made)
|
||||
assert len(got) == len(want), f"{what}: {made.size}, not {px}"
|
||||
off = sum(abs(a - b) for a, b in zip(got, want)) / len(got)
|
||||
assert off < most, f"{what} is not its master (off {off:.2f})"
|
||||
# Shapeless: the mark alone, no container behind it.
|
||||
mark = Image.open(f"{icons}/{slug}.png")
|
||||
same(mark, 512, "plain", "png")
|
||||
assert mark.convert("RGBA").getpixel((0, 0))[3] == 0, "a container behind the mark"
|
||||
ico = Image.open(f"{icons}/{slug}.ico")
|
||||
assert sorted(ico.info["sizes"]) == [(s, s) for s in (16, 24, 32, 48, 64, 128, 256)], ico.info["sizes"]
|
||||
for size in ico.info["sizes"]:
|
||||
layer = ico.ico.getimage(size)
|
||||
same(layer, size[0], "plain", f"ico {size[0]}")
|
||||
corners = [(0, 0), (size[0] - 1, 0), (0, size[0] - 1), (size[0] - 1, size[0] - 1)]
|
||||
assert all(layer.convert("RGBA").getpixel(at)[3] == 0 for at in corners), f"ico {size[0]}: a container"
|
||||
names = sorted(os.listdir(f"{work}/{slug}.iconset"))
|
||||
assert len(names) == 10, names
|
||||
for name in names:
|
||||
made = Image.open(f"{work}/{slug}.iconset/{name}")
|
||||
# macOS stores 16 and 32 px lossily.
|
||||
same(made, made.width, "macos", f"icns {name}", 8.0 if name in ("icon_16x16.png", "icon_32x32.png") else 1.0)
|
||||
# Squircle: Apple's margin is clear, the body's middle is solid.
|
||||
big = Image.open(f"{work}/{slug}.iconset/icon_512x512@2x.png").convert("RGBA")
|
||||
assert big.getpixel((40, 512))[3] == 0 and big.getpixel((150, 150))[3] == 0, "not a squircle"
|
||||
assert big.getpixel((512, 110))[3] == 255, "no body"
|
||||
EOF
|
||||
count=$((count + 1))
|
||||
done <"$root/packaging/apps.tsv"
|
||||
[ "$count" -eq 8 ] || fail "checked $count apps, expected 8"
|
||||
echo "icons: 8 apps, squircle on macOS, shapeless on Linux and Windows"
|
||||
Reference in new issue
Block a user