feat(brand): Mark M is the Furtka logo — website, installer, box UI, console, boot splash
All checks were successful
CI / lint (pull_request) Successful in 30s
CI / validate-json (pull_request) Successful in 24s
CI / markdown-links (pull_request) Successful in 16s
CI / test (pull_request) Successful in 1m47s
CI / lint (push) Successful in 28s
CI / test (push) Successful in 1m56s
CI / validate-json (push) Successful in 24s
CI / markdown-links (push) Successful in 21s
Deploy site / deploy (push) Successful in 5s
Build ISO / build-iso (push) Successful in 20m27s
The logo vote on furtka.org closed with M 29 · R 13 (42 votes). - website/tools/brand-assets.py generates every brand file from the logo-assets.py geometry: favicons (small-size tier, accent with a dark-mode variant) for website / webinstaller / on-box UI, 180 px apple-touch-icon, 1200x630 default share card, 640x480 syslinux splash, and docs/brand/ (mark, small mark, lockups, 20-column console art) - website: header mark, favicon, touch icon, default og:image; /logo/ now shows the final result (static counts, winner highlighted, no voting UI or poll.js); homepage link reads 'meet our new logo' - webinstaller: M in the header + tab icon - on-box UI: .brand mark via CSS mask in brand red (not the UI's green), favicon on every page, brand line on the login page - console: M art above the text of /etc/issue on the installed box (furtka-welcome) and the live ISO (furtka-update-issue) - ISO: build.sh swaps releng's Arch splash for the Furtka one - README shows the lockup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wq1hzUp4ZmCKHDuLKCnu73
12
CHANGELOG.md
|
|
@ -7,6 +7,18 @@ This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, r
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **New logo: Mark M** (a lowercase *f* cut out of an arched gate), winner of
|
||||||
|
the furtka.org vote (29 of 42 votes). It is now in the website header,
|
||||||
|
favicon and share card, the installer header and tab icon, the on-box UI
|
||||||
|
(every page and the login), the console welcome screens of the live ISO
|
||||||
|
and the installed box, and the BIOS boot-menu splash. Brand files in
|
||||||
|
`docs/brand/`, generated by `website/tools/brand-assets.py`.
|
||||||
|
- **furtka.org/download/** (and `/de/download/`): ISO download with size,
|
||||||
|
date and checksum, requirements and step-by-step install guide. Releases
|
||||||
|
now publish `furtka-<ver>.iso.sha256` and refresh the site.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
- **App names are validated everywhere.** `POST /api/apps/remove` with
|
- **App names are validated everywhere.** `POST /api/apps/remove` with
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
<p><img src="docs/brand/furtka-lockup.svg" alt="Furtka" height="56"></p>
|
||||||
|
|
||||||
# Furtka
|
# Furtka
|
||||||
|
|
||||||
**Open-source home server OS — simple enough for everyone.** · [furtka.org](https://furtka.org)
|
**Open-source home server OS — simple enough for everyone.** · [furtka.org](https://furtka.org)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,17 @@ hostname=$(cat /etc/hostname 2>/dev/null || hostname)
|
||||||
mapfile -t ips < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
mapfile -t ips < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
||||||
|
|
||||||
new=$(
|
new=$(
|
||||||
|
# Furtka mark (docs/brand/console-banner.txt, from brand-assets.py)
|
||||||
|
echo ' ▄▄▄▄▄▄▄▄'
|
||||||
|
echo ' ▄██████████▄'
|
||||||
|
echo ' █████▀▀▀▀▀▀███'
|
||||||
|
echo ' ████▀ ████'
|
||||||
|
echo ' ████ █████████'
|
||||||
|
echo ' ████ ████'
|
||||||
|
echo ' ████ ▄▄▄▄▄████'
|
||||||
|
echo ' ████ █████████'
|
||||||
|
echo ' ████ █████████'
|
||||||
|
echo ' ▀▀▀▀ ▀▀▀▀▀▀▀▀▀'
|
||||||
echo
|
echo
|
||||||
echo " Furtka is ready."
|
echo " Furtka is ready."
|
||||||
echo
|
echo
|
||||||
|
|
|
||||||
1
assets/www/favicon.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><style>svg{color:#c03a28}@media (prefers-color-scheme:dark){svg{color:#ff6b56}}</style><path fill="currentColor" fill-rule="evenodd" d="M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 316 B |
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
<title>Install local HTTPS · Furtka</title>
|
<title>Install local HTTPS · Furtka</title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
<title>Furtka</title>
|
<title>Furtka</title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
<title>Settings · Furtka</title>
|
<title>Settings · Furtka</title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
|
|
||||||
|
|
@ -73,13 +73,17 @@ body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.55rem;
|
gap: 0.55rem;
|
||||||
}
|
}
|
||||||
|
/* Furtka mark (M), drawn as a mask so it takes the accent colour in both
|
||||||
|
themes. Generated geometry: website/tools/brand-assets.py. */
|
||||||
.brand::before {
|
.brand::before {
|
||||||
content: "";
|
content: "";
|
||||||
width: 0.7rem;
|
width: 1.35rem;
|
||||||
height: 0.7rem;
|
height: 1.35rem;
|
||||||
background: var(--accent);
|
/* Brand red, not the UI's green accent — the logo keeps its colour.
|
||||||
border-radius: 2px;
|
This stylesheet is dark-first; the light theme gets the deeper red. */
|
||||||
transform: rotate(45deg);
|
background: #ff6b56;
|
||||||
|
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill-rule='evenodd' d='M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z'/%3E%3C/svg%3E") center / contain no-repeat;
|
||||||
|
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill-rule='evenodd' d='M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z'/%3E%3C/svg%3E") center / contain no-repeat;
|
||||||
}
|
}
|
||||||
.nav-links { display: flex; gap: 0.25rem; }
|
.nav-links { display: flex; gap: 0.25rem; }
|
||||||
.nav-links a {
|
.nav-links a {
|
||||||
|
|
@ -490,3 +494,8 @@ details.log-details[open] > summary { color: var(--fg); }
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
.app-icon svg { width: 36px; height: 36px; }
|
.app-icon svg { width: 36px; height: 36px; }
|
||||||
|
|
||||||
|
.login-brand { margin: 0 0 1.5rem; font-size: 1.2rem; }
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.brand::before { background: #c03a28; }
|
||||||
|
}
|
||||||
|
|
|
||||||
19
docs/brand/README.md
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Furtka brand files
|
||||||
|
|
||||||
|
The Furtka logo is **Mark M** — a lowercase *f* cut out of an arched gate. It won the
|
||||||
|
public logo vote on furtka.org (closed 2026-09-28: M 29, R 13 of 42 votes).
|
||||||
|
|
||||||
|
| File | Use |
|
||||||
|
|---|---|
|
||||||
|
| `furtka-mark.svg` | The mark, 32 px and up |
|
||||||
|
| `furtka-mark-small.svg` | Simplified drawing for 16–24 px (favicons, tab icons) |
|
||||||
|
| `furtka-lockup.svg` | Mark + wordmark, accent colour on light grounds |
|
||||||
|
| `furtka-lockup-white.svg` | Mark + wordmark in white, for dark or accent grounds |
|
||||||
|
| `console-banner.txt` | Text-mode art for the console welcome screens |
|
||||||
|
|
||||||
|
Colours: accent `#c03a28` (dark UI: `#ff6b56`), ink `#0d0d0f`.
|
||||||
|
|
||||||
|
Everything here and the copies in `website/static/`, `webinstaller/static/`,
|
||||||
|
`assets/www/` and `iso/overlay/splash.png` are generated by
|
||||||
|
`website/tools/brand-assets.py` from the geometry in `website/tools/logo-assets.py`.
|
||||||
|
Change the geometry there and re-run the script — don't edit the outputs by hand.
|
||||||
10
docs/brand/console-banner.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
▄▄▄▄▄▄▄▄
|
||||||
|
▄██████████▄
|
||||||
|
█████▀▀▀▀▀▀███
|
||||||
|
████▀ ████
|
||||||
|
████ █████████
|
||||||
|
████ ████
|
||||||
|
████ ▄▄▄▄▄████
|
||||||
|
████ █████████
|
||||||
|
████ █████████
|
||||||
|
▀▀▀▀ ▀▀▀▀▀▀▀▀▀
|
||||||
1
docs/brand/furtka-lockup-white.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -4 232.44 64.00" role="img" aria-label="Furtka"><g transform="scale(1.0000) translate(-6 -2)"><path fill="#ffffff" fill-rule="evenodd" d="M6 60V28a26 26 0 0 1 52 0v32Z M20.0 60V26.5A10.5 10.5 0 0 1 30.5 16H45.0v8.6H32.5a3.5 3.5 0 0 0-3.5 3.5V32h16v8.6H29.0V60Z"/></g><g fill="#ffffff"><path transform="translate(68.24 58.00) scale(0.05354 -0.05354)" d="M115 0V494H30V600H115V637Q115 687 132.5 724.0Q150 761 192.0 781.5Q234 802 305 802Q333 802 358 799V695Q344 698 324 698Q287 698 273.5 682.0Q260 666 260 632V600H358V494H260V0Z"/><path transform="translate(87.21 58.00) scale(0.05354 -0.05354)" d="M271 -15Q169 -15 119.0 48.5Q69 112 69 218V600H218V244Q218 179 241.5 143.0Q265 107 316 107Q379 107 413.0 156.0Q447 205 447 276V600H596V0H462V90H438Q423 44 382.5 14.5Q342 -15 271 -15Z"/><path transform="translate(121.45 58.00) scale(0.05354 -0.05354)" d="M76 0V600H211V492H235Q241 521 257.0 547.0Q273 573 307.0 589.5Q341 606 399 606H428V471H387Q301 471 263.0 427.0Q225 383 225 294V0Z"/><path transform="translate(144.17 58.00) scale(0.05354 -0.05354)" d="M162 0Q139 27 125.5 65.0Q112 103 112 179V494H30V600H114V750H259V600H358V494H260V196Q260 116 278.0 74.5Q296 33 321 6V0Z"/><path transform="translate(163.14 58.00) scale(0.05354 -0.05354)" d="M78 0V800H226V366H274L449 600H608V594L397 320L632 6V0H457L274 254H226V0Z"/><path transform="translate(195.88 58.00) scale(0.05354 -0.05354)" d="M280 -13Q198 -13 144.0 27.5Q90 68 63.5 138.5Q37 209 37 301Q37 395 64.0 465.0Q91 535 145.0 574.0Q199 613 280 613Q344 613 384.5 587.5Q425 562 445 522H469V600H606V0H469V78H445Q425 41 385.0 14.0Q345 -13 280 -13ZM317 95Q381 95 419.5 151.0Q458 207 458 300Q458 394 419.5 449.5Q381 505 317 505Q257 505 223.5 453.0Q190 401 190 300Q190 199 223.5 147.0Q257 95 317 95Z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
1
docs/brand/furtka-lockup.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -4 232.44 64.00" role="img" aria-label="Furtka"><g transform="scale(1.0000) translate(-6 -2)"><path fill="#c03a28" fill-rule="evenodd" d="M6 60V28a26 26 0 0 1 52 0v32Z M20.0 60V26.5A10.5 10.5 0 0 1 30.5 16H45.0v8.6H32.5a3.5 3.5 0 0 0-3.5 3.5V32h16v8.6H29.0V60Z"/></g><g fill="#c03a28"><path transform="translate(68.24 58.00) scale(0.05354 -0.05354)" d="M115 0V494H30V600H115V637Q115 687 132.5 724.0Q150 761 192.0 781.5Q234 802 305 802Q333 802 358 799V695Q344 698 324 698Q287 698 273.5 682.0Q260 666 260 632V600H358V494H260V0Z"/><path transform="translate(87.21 58.00) scale(0.05354 -0.05354)" d="M271 -15Q169 -15 119.0 48.5Q69 112 69 218V600H218V244Q218 179 241.5 143.0Q265 107 316 107Q379 107 413.0 156.0Q447 205 447 276V600H596V0H462V90H438Q423 44 382.5 14.5Q342 -15 271 -15Z"/><path transform="translate(121.45 58.00) scale(0.05354 -0.05354)" d="M76 0V600H211V492H235Q241 521 257.0 547.0Q273 573 307.0 589.5Q341 606 399 606H428V471H387Q301 471 263.0 427.0Q225 383 225 294V0Z"/><path transform="translate(144.17 58.00) scale(0.05354 -0.05354)" d="M162 0Q139 27 125.5 65.0Q112 103 112 179V494H30V600H114V750H259V600H358V494H260V196Q260 116 278.0 74.5Q296 33 321 6V0Z"/><path transform="translate(163.14 58.00) scale(0.05354 -0.05354)" d="M78 0V800H226V366H274L449 600H608V594L397 320L632 6V0H457L274 254H226V0Z"/><path transform="translate(195.88 58.00) scale(0.05354 -0.05354)" d="M280 -13Q198 -13 144.0 27.5Q90 68 63.5 138.5Q37 209 37 301Q37 395 64.0 465.0Q91 535 145.0 574.0Q199 613 280 613Q344 613 384.5 587.5Q425 562 445 522H469V600H606V0H469V78H445Q425 41 385.0 14.0Q345 -13 280 -13ZM317 95Q381 95 419.5 151.0Q458 207 458 300Q458 394 419.5 449.5Q381 505 317 505Q257 505 223.5 453.0Q190 401 190 300Q190 199 223.5 147.0Q257 95 317 95Z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
1
docs/brand/furtka-mark-small.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="#c03a28" fill-rule="evenodd" d="M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 224 B |
1
docs/brand/furtka-mark.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="#c03a28" fill-rule="evenodd" d="M6 60V28a26 26 0 0 1 52 0v32Z M20.0 60V26.5A10.5 10.5 0 0 1 30.5 16H45.0v8.6H32.5a3.5 3.5 0 0 0-3.5 3.5V32h16v8.6H29.0V60Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 236 B |
|
|
@ -73,6 +73,7 @@ _HTML = """<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
<title>Furtka Apps</title>
|
<title>Furtka Apps</title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
|
@ -557,12 +558,14 @@ _HTML_LOGIN = """<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
<title>Furtka · {{ TITLE }}</title>
|
<title>Furtka · {{ TITLE }}</title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="wrap login-wrap">
|
<main class="wrap login-wrap">
|
||||||
|
<p class="brand login-brand">Furtka</p>
|
||||||
<h1>{{ HEADING }}</h1>
|
<h1>{{ HEADING }}</h1>
|
||||||
<p class="lede">{{ LEDE }}</p>
|
<p class="lede">{{ LEDE }}</p>
|
||||||
<form id="login-form" onsubmit="return doLogin(event)">
|
<form id="login-form" onsubmit="return doLogin(event)">
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@ cp -a "$SCRIPT_DIR/overlay/airootfs/." "$PROFILE_WORK/airootfs/"
|
||||||
rm -rf "$PROFILE_WORK/airootfs/etc/systemd/system/getty@tty1.service.d"
|
rm -rf "$PROFILE_WORK/airootfs/etc/systemd/system/getty@tty1.service.d"
|
||||||
|
|
||||||
echo "==> Rebranding boot menu (GRUB + syslinux + systemd-boot)"
|
echo "==> Rebranding boot menu (GRUB + syslinux + systemd-boot)"
|
||||||
|
# BIOS boot menu background: Furtka lockup instead of the Arch logo
|
||||||
|
# (generated by website/tools/brand-assets.py, 640x480 as syslinux wants).
|
||||||
|
cp "$SCRIPT_DIR/overlay/splash.png" "$PROFILE_WORK/syslinux/splash.png"
|
||||||
# releng ships menu entries labelled "Arch Linux install medium" across three
|
# releng ships menu entries labelled "Arch Linux install medium" across three
|
||||||
# bootloader configs (BIOS syslinux, GRUB, systemd-boot for UEFI). Rewrite to
|
# bootloader configs (BIOS syslinux, GRUB, systemd-boot for UEFI). Rewrite to
|
||||||
# our brand. Done with sed (not a static overlay) so upstream archiso file
|
# our brand. Done with sed (not a static overlay) so upstream archiso file
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,17 @@ issue=/etc/issue
|
||||||
mapfile -t ips < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
mapfile -t ips < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
||||||
|
|
||||||
new=$(
|
new=$(
|
||||||
|
# Furtka mark (docs/brand/console-banner.txt, from brand-assets.py)
|
||||||
|
echo ' ▄▄▄▄▄▄▄▄'
|
||||||
|
echo ' ▄██████████▄'
|
||||||
|
echo ' █████▀▀▀▀▀▀███'
|
||||||
|
echo ' ████▀ ████'
|
||||||
|
echo ' ████ █████████'
|
||||||
|
echo ' ████ ████'
|
||||||
|
echo ' ████ ▄▄▄▄▄████'
|
||||||
|
echo ' ████ █████████'
|
||||||
|
echo ' ████ █████████'
|
||||||
|
echo ' ▀▀▀▀ ▀▀▀▀▀▀▀▀▀'
|
||||||
echo
|
echo
|
||||||
echo " Open Furtka in a browser on another device on your network:"
|
echo " Open Furtka in a browser on another device on your network:"
|
||||||
echo
|
echo
|
||||||
|
|
|
||||||
BIN
iso/overlay/splash.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
1
webinstaller/static/favicon.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><style>svg{color:#c03a28}@media (prefers-color-scheme:dark){svg{color:#ff6b56}}</style><path fill="currentColor" fill-rule="evenodd" d="M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 316 B |
|
|
@ -4,6 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% block title %}Furtka Installer{% endblock %}</title>
|
<title>{% block title %}Furtka Installer{% endblock %}</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
{% block head_extra %}{% endblock %}
|
{% block head_extra %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -11,12 +12,8 @@
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a href="{{ url_for('install_step_1') }}" class="site-title" aria-label="Furtka Installer — restart">
|
<a href="{{ url_for('install_step_1') }}" class="site-title" aria-label="Furtka Installer — restart">
|
||||||
<svg class="gate-mark" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
<svg class="gate-mark" width="24" height="24" viewBox="0 0 64 64" aria-hidden="true">
|
||||||
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
<path fill="currentColor" fill-rule="evenodd" d="M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z"/>
|
||||||
<path d="M4 20 V12 A9 9 0 0 1 20 12 V20"/>
|
|
||||||
<line x1="12" y1="5" x2="12" y2="20"/>
|
|
||||||
<line x1="3" y1="20" x2="21" y2="20"/>
|
|
||||||
<line x1="15" y1="12" x2="15" y2="14.5"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
<span class="wordmark">Furtka<span class="sep">·</span>Installer</span>
|
<span class="wordmark">Furtka<span class="sep">·</span>Installer</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -693,6 +693,16 @@ kbd {
|
||||||
.poll-bar { height: 0.45rem; border-radius: 999px; background: var(--bg-subtle); overflow: hidden; display: none; }
|
.poll-bar { height: 0.45rem; border-radius: 999px; background: var(--bg-subtle); overflow: hidden; display: none; }
|
||||||
.poll-bar i { display: block; height: 100%; width: 0; background: var(--accent); transition: width 500ms ease; }
|
.poll-bar i { display: block; height: 100%; width: 0; background: var(--accent); transition: width 500ms ease; }
|
||||||
.poll--voted .poll-bar { display: block; }
|
.poll--voted .poll-bar { display: block; }
|
||||||
|
.poll-bar--final { display: block; }
|
||||||
|
.poll-result {
|
||||||
|
margin: 1.5rem 0 0;
|
||||||
|
padding: 1rem 1.2rem;
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
background: var(--bg-subtle);
|
||||||
|
border-radius: 0 0.6rem 0.6rem 0;
|
||||||
|
max-width: 40rem;
|
||||||
|
}
|
||||||
|
.poll-result strong { color: var(--accent); }
|
||||||
.poll-count { margin: 0; min-height: 1.4em; font-size: 0.85rem; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
|
.poll-count { margin: 0; min-height: 1.4em; font-size: 0.85rem; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
|
||||||
.poll-status { margin: 1.25rem 0 0; min-height: 1.5em; color: var(--fg-muted); }
|
.poll-status { margin: 1.25rem 0 0; min-height: 1.5em; color: var(--fg-muted); }
|
||||||
.poll-note { margin-top: 1rem; max-width: var(--measure); font-size: 0.85rem; color: var(--fg-muted); }
|
.poll-note { margin-top: 1rem; max-width: var(--measure); font-size: 0.85rem; color: var(--fg-muted); }
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ status_text: "in Arbeit"
|
||||||
cta_download: "ISO herunterladen"
|
cta_download: "ISO herunterladen"
|
||||||
cta_source: "Quellcode"
|
cta_source: "Quellcode"
|
||||||
poll_link: "/de/logo/"
|
poll_link: "/de/logo/"
|
||||||
poll_text: "Wir suchen ein Logo — hilf uns beim Aussuchen"
|
poll_text: "Abgestimmt — das ist unser neues Logo"
|
||||||
# features_today / features_next müssen index-parallel zu content/_index.md bleiben.
|
# features_today / features_next müssen index-parallel zu content/_index.md bleiben.
|
||||||
intro: |
|
intro: |
|
||||||
**Furtka** ist ein offenes Heimserver-Betriebssystem.
|
**Furtka** ist ein offenes Heimserver-Betriebssystem.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ status_text: "work in progress"
|
||||||
cta_download: "Download ISO"
|
cta_download: "Download ISO"
|
||||||
cta_source: "Source code"
|
cta_source: "Source code"
|
||||||
poll_link: "/logo/"
|
poll_link: "/logo/"
|
||||||
poll_text: "We're picking a logo — help us choose"
|
poll_text: "The vote is in — meet our new logo"
|
||||||
# Keep features_today / features_next index-aligned with content/_index.de.md.
|
# Keep features_today / features_next index-aligned with content/_index.de.md.
|
||||||
intro: |
|
intro: |
|
||||||
**Furtka** is an open-source home server OS.
|
**Furtka** is an open-source home server OS.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
---
|
---
|
||||||
title: "Welches Logo?"
|
title: "Unser neues Logo"
|
||||||
description: "Zwei Vorschläge für das Furtka-Zeichen. Wähl das, das dir besser gefällt — ein Klick genügt."
|
description: "Zwei Vorschläge für das Furtka-Zeichen, und ihr habt gewählt. So ist die Abstimmung ausgegangen — und so sähe jedes Zeichen überall aus, wo es funktionieren müsste."
|
||||||
|
closed: true
|
||||||
|
result: "Die Abstimmung ist beendet — **M hat mit 29 von 42 Stimmen gewonnen** (R: 13). Danke an alle, die abgestimmt haben! M ist jetzt das Furtka-Logo."
|
||||||
|
final:
|
||||||
|
m: 29
|
||||||
|
r: 13
|
||||||
translationKey: "logo"
|
translationKey: "logo"
|
||||||
layout: single
|
layout: single
|
||||||
type: poll
|
type: poll
|
||||||
|
|
@ -17,7 +22,7 @@ r:
|
||||||
title: "R — Fenster, Rad und F"
|
title: "R — Fenster, Rad und F"
|
||||||
text: "Ein Bogenfenster mit einem Speichenrad dahinter; in der oberen rechten Scheibe wird die Speiche zum F."
|
text: "Ein Bogenfenster mit einem Speichenrad dahinter; in der oberen rechten Scheibe wird die Speiche zum F."
|
||||||
show:
|
show:
|
||||||
intro: "Noch unentschieden? Hier ist jedes Zeichen überall dort, wo es funktionieren muss."
|
intro: "Beide Zeichen überall dort, wo sie hätten funktionieren müssen."
|
||||||
sizes: "Groß und klein"
|
sizes: "Groß und klein"
|
||||||
sizes_text: "Vom Poster bis zum 16-Pixel-Symbol im Browser-Tab. Unter 32 Pixeln wechseln beide auf eine vereinfachte Zeichnung — M verliert nichts, R behält Fenster, Kreuz und Nabe."
|
sizes_text: "Vom Poster bis zum 16-Pixel-Symbol im Browser-Tab. Unter 32 Pixeln wechseln beide auf eine vereinfachte Zeichnung — M verliert nichts, R behält Fenster, Kreuz und Nabe."
|
||||||
name: "Mit dem Namen"
|
name: "Mit dem Namen"
|
||||||
|
|
@ -48,6 +53,6 @@ labels:
|
||||||
*Furtka* ist polnisch für die kleine Gartenpforte — die Tür im Zaun, durch
|
*Furtka* ist polnisch für die kleine Gartenpforte — die Tür im Zaun, durch
|
||||||
die man hineinkommt, ohne das große Tor aufzumachen. Beide Zeichen spielen
|
die man hineinkommt, ohne das große Tor aufzumachen. Beide Zeichen spielen
|
||||||
damit: das eine zeichnet den Buchstaben ins Tor, das andere schaut durchs
|
damit: das eine zeichnet den Buchstaben ins Tor, das andere schaut durchs
|
||||||
Tor auf ein Rad. Stimm direkt hier ab — oder scroll erst weiter und sieh
|
Tor auf ein Rad. Unten stehen beide mit dem Endergebnis — und jedes Zeichen
|
||||||
beide Zeichen überall, wo sie funktionieren müssen, vom Poster bis zum
|
überall dort, wo es hätte funktionieren müssen, vom Poster bis zum winzigen
|
||||||
winzigen Symbol im Browser-Tab.
|
Symbol im Browser-Tab.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
---
|
---
|
||||||
title: "Which logo?"
|
title: "Our new logo"
|
||||||
description: "Two proposals for the Furtka mark. Pick the one you like better — it takes one click."
|
description: "Two proposals for the Furtka mark, and you picked one. Here is how the vote ended — and each mark everywhere it would have had to work."
|
||||||
|
closed: true
|
||||||
|
result: "The vote is closed — **M won with 29 of 42 votes** (R: 13). Thank you to everyone who voted! M is now the Furtka logo."
|
||||||
|
final:
|
||||||
|
m: 29
|
||||||
|
r: 13
|
||||||
translationKey: "logo"
|
translationKey: "logo"
|
||||||
url: /logo/
|
url: /logo/
|
||||||
layout: single
|
layout: single
|
||||||
|
|
@ -18,7 +23,7 @@ r:
|
||||||
title: "R — window, wheel and F"
|
title: "R — window, wheel and F"
|
||||||
text: "An arched window with a wheel of spokes behind it; in the top-right pane the spoke becomes an F."
|
text: "An arched window with a wheel of spokes behind it; in the top-right pane the spoke becomes an F."
|
||||||
show:
|
show:
|
||||||
intro: "Not sure yet? Here is each mark everywhere it would have to work."
|
intro: "Both marks everywhere they would have had to work."
|
||||||
sizes: "Big and small"
|
sizes: "Big and small"
|
||||||
sizes_text: "From a poster to the 16-pixel icon in a browser tab. Below 32 pixels both marks switch to a simplified drawing — M drops nothing, R keeps the window, the cross and the hub."
|
sizes_text: "From a poster to the 16-pixel icon in a browser tab. Below 32 pixels both marks switch to a simplified drawing — M drops nothing, R keeps the window, the cross and the hub."
|
||||||
name: "With the name"
|
name: "With the name"
|
||||||
|
|
@ -49,5 +54,5 @@ labels:
|
||||||
*Furtka* is Polish for a small garden gate — the little door in the fence
|
*Furtka* is Polish for a small garden gate — the little door in the fence
|
||||||
that lets you in without opening the whole thing. Both marks play with that:
|
that lets you in without opening the whole thing. Both marks play with that:
|
||||||
one draws the letter into the gate, the other looks through the gate at a
|
one draws the letter into the gate, the other looks through the gate at a
|
||||||
wheel. Vote right here — or scroll on first and see both marks everywhere
|
wheel. Below are both, with the final count — and each mark everywhere it
|
||||||
they would have to work, from a poster to the tiny icon in a browser tab.
|
would have had to work, from a poster to the tiny icon in a browser tab.
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<a class="cta cta--ghost" href="{{ site.Params.forgejoURL }}/{{ site.Params.repoPath }}">{{ .Params.cta_source }}</a>
|
<a class="cta cta--ghost" href="{{ site.Params.forgejoURL }}/{{ site.Params.repoPath }}">{{ .Params.cta_source }}</a>
|
||||||
</p>
|
</p>
|
||||||
{{ with .Params.poll_link }}
|
{{ with .Params.poll_link }}
|
||||||
<p class="reveal"><a class="home-poll" href="{{ . }}">{{ partial "mark-m.html" (dict "class" "" "id" "hm") }}{{ partial "mark-r.html" (dict "class" "" "id" "hr") }}<span>{{ $.Params.poll_text }} →</span></a></p>
|
<p class="reveal"><a class="home-poll" href="{{ . }}">{{ partial "mark-m.html" (dict "class" "" "id" "hm") }}<span>{{ $.Params.poll_text }} →</span></a></p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,2 @@
|
||||||
<svg class="gate-mark" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
{{- /* Furtka mark (M, small-size tier) — generated by tools/brand-assets.py from logo-assets.py geometry. */ -}}
|
||||||
<path d="M4 20 V12 A9 9 0 0 1 20 12 V20"/>
|
<svg class="gate-mark" width="1em" height="1em" viewBox="0 0 64 64" aria-hidden="true" focusable="false"><path fill="currentColor" fill-rule="evenodd" d="M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z"/></svg>
|
||||||
<line x1="12" y1="5" x2="12" y2="20"/>
|
|
||||||
<line x1="3" y1="20" x2="21" y2="20"/>
|
|
||||||
<line x1="15" y1="12" x2="15" y2="14.5"/>
|
|
||||||
</svg>
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 384 B After Width: | Height: | Size: 390 B |
|
|
@ -10,12 +10,13 @@
|
||||||
<meta name="theme-color" content="#f7f6f3" media="(prefers-color-scheme: light)">
|
<meta name="theme-color" content="#f7f6f3" media="(prefers-color-scheme: light)">
|
||||||
<meta name="theme-color" content="#0d0d0f" media="(prefers-color-scheme: dark)">
|
<meta name="theme-color" content="#0d0d0f" media="(prefers-color-scheme: dark)">
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
<meta property="og:site_name" content="{{ site.Title }}">
|
<meta property="og:site_name" content="{{ site.Title }}">
|
||||||
<meta property="og:title" content="{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}">
|
<meta property="og:title" content="{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}">
|
||||||
<meta property="og:description" content="{{ with .Params.description }}{{ . }}{{ else }}{{ site.Params.description }}{{ end }}">
|
<meta property="og:description" content="{{ with .Params.description }}{{ . }}{{ else }}{{ site.Params.description }}{{ end }}">
|
||||||
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
||||||
<meta property="og:url" content="{{ .Permalink }}">
|
<meta property="og:url" content="{{ .Permalink }}">
|
||||||
{{ with .Params.image }}<meta property="og:image" content="{{ . | absURL }}">
|
{{ with .Params.image | default "/img/brand/social.png" }}<meta property="og:image" content="{{ . | absURL }}">
|
||||||
<meta property="og:image:width" content="1200">
|
<meta property="og:image:width" content="1200">
|
||||||
<meta property="og:image:height" content="630">
|
<meta property="og:image:height" content="630">
|
||||||
<meta name="twitter:card" content="summary_large_image">{{ end }}
|
<meta name="twitter:card" content="summary_large_image">{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,43 @@
|
||||||
side gets the "first thing on the page" advantage for the whole poll. */}}
|
side gets the "first thing on the page" advantage for the whole poll. */}}
|
||||||
{{- $order := $p.order | default (slice "m" "r") -}}
|
{{- $order := $p.order | default (slice "m" "r") -}}
|
||||||
{{- $s := $p.show -}}
|
{{- $s := $p.show -}}
|
||||||
<article class="poll" data-poll="{{ $p.poll }}" data-api="/api/poll/{{ $p.poll }}">
|
<article class="{{ if $p.closed }}poll-closed{{ else }}poll{{ end }}" data-poll="{{ $p.poll }}" data-api="/api/poll/{{ $p.poll }}">
|
||||||
<header class="page-header">
|
<header class="page-header">
|
||||||
<h1>{{ .Title }}</h1>
|
<h1>{{ .Title }}</h1>
|
||||||
{{ with $p.description }}<p class="lede">{{ . }}</p>{{ end }}
|
{{ with $p.description }}<p class="lede">{{ . }}</p>{{ end }}
|
||||||
</header>
|
</header>
|
||||||
|
{{ with $p.result }}<p class="poll-result" role="status">{{ . | markdownify }}</p>{{ end }}
|
||||||
<div class="prose">{{ .Content }}</div>
|
<div class="prose">{{ .Content }}</div>
|
||||||
|
|
||||||
{{/* ── Quick vote ─────────────────────────────────────────── */}}
|
{{/* ── Quick vote ─────────────────────────────────────────── */}}
|
||||||
<div class="poll-grid" id="vote">
|
<div class="poll-grid" id="vote">
|
||||||
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
|
{{ range $k := $order }}{{ $d := index site.Data.logo $k }}
|
||||||
{{ $c := index $p $k }}
|
{{ $c := index $p $k }}
|
||||||
<section class="poll-card" data-choice="{{ $k }}" aria-labelledby="poll-{{ $k }}-title">
|
{{- $total := 0 }}{{ range $v := $p.final }}{{ $total = add $total $v }}{{ end }}
|
||||||
|
{{- $won := and $p.closed (eq $k (cond (gt (index $p.final "m") (index $p.final "r")) "m" "r")) }}
|
||||||
|
<section class="poll-card{{ if $won }} is-yours{{ end }}" data-choice="{{ $k }}" aria-labelledby="poll-{{ $k }}-title">
|
||||||
<div class="poll-art poll-art--{{ $k }}">{{ $d.display | safeHTML }}</div>
|
<div class="poll-art poll-art--{{ $k }}">{{ $d.display | safeHTML }}</div>
|
||||||
<h2 id="poll-{{ $k }}-title">{{ $c.title }}</h2>
|
<h2 id="poll-{{ $k }}-title">{{ $c.title }}</h2>
|
||||||
<p class="poll-desc">{{ $c.text }}</p>
|
<p class="poll-desc">{{ $c.text }}</p>
|
||||||
|
{{ if $p.closed }}
|
||||||
|
{{ $n := index $p.final $k }}
|
||||||
|
<div class="poll-bar poll-bar--final" aria-hidden="true"><i style="width: {{ div (mul $n 100) $total }}%"></i></div>
|
||||||
|
<p class="poll-count">{{ $n }} / {{ $total }}</p>
|
||||||
|
{{ else }}
|
||||||
<button type="button" class="poll-btn" data-vote="{{ $k }}">{{ $p.labels.vote }}</button>
|
<button type="button" class="poll-btn" data-vote="{{ $k }}">{{ $p.labels.vote }}</button>
|
||||||
<div class="poll-bar" aria-hidden="true"><i></i></div>
|
<div class="poll-bar" aria-hidden="true"><i></i></div>
|
||||||
<p class="poll-count" data-count="{{ $k }}"></p>
|
<p class="poll-count" data-count="{{ $k }}"></p>
|
||||||
|
{{ end }}
|
||||||
</section>
|
</section>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
{{ if not $p.closed }}
|
||||||
<p class="poll-status" role="status" aria-live="polite"
|
<p class="poll-status" role="status" aria-live="polite"
|
||||||
data-t-voted="{{ $p.labels.voted }}"
|
data-t-voted="{{ $p.labels.voted }}"
|
||||||
data-t-total="{{ $p.labels.total }}"
|
data-t-total="{{ $p.labels.total }}"
|
||||||
data-t-error="{{ $p.labels.error }}"
|
data-t-error="{{ $p.labels.error }}"
|
||||||
data-t-change="{{ $p.labels.change }}">{{ $p.labels.nojs }}</p>
|
data-t-change="{{ $p.labels.change }}">{{ $p.labels.nojs }}</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{/* ── Showcase ───────────────────────────────────────────── */}}
|
{{/* ── Showcase ───────────────────────────────────────────── */}}
|
||||||
<div class="show">
|
<div class="show">
|
||||||
|
|
@ -148,6 +159,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/* ── Vote again at the end ─────────────────────────────── */}}
|
{{/* ── Vote again at the end ─────────────────────────────── */}}
|
||||||
|
{{ if not $p.closed }}
|
||||||
<section class="poll-final">
|
<section class="poll-final">
|
||||||
<h2 class="show-h">{{ $s.final }}</h2>
|
<h2 class="show-h">{{ $s.final }}</h2>
|
||||||
<div class="poll-final-row">
|
<div class="poll-final-row">
|
||||||
|
|
@ -162,7 +174,10 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<p class="poll-note">{{ $p.labels.note | safeHTML }}</p>
|
<p class="poll-note">{{ $p.labels.note | safeHTML }}</p>
|
||||||
|
{{ end }}
|
||||||
</article>
|
</article>
|
||||||
|
{{ if not $p.closed }}
|
||||||
{{ $js := resources.Get "js/poll.js" | minify | fingerprint }}
|
{{ $js := resources.Get "js/poll.js" | minify | fingerprint }}
|
||||||
<script defer src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}"></script>
|
<script defer src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
|
||||||
BIN
website/static/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,6 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#a33e2e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><style>svg{color:#c03a28}@media (prefers-color-scheme:dark){svg{color:#ff6b56}}</style><path fill="currentColor" fill-rule="evenodd" d="M5 60V27a27 27 0 0 1 54 0v33Z M19.5 60V26A10 10 0 0 1 29.5 16H46v10H32a2.5 2.5 0 0 0-2.5 2.5V32H46v10H29.5v18Z"/></svg>
|
||||||
<path d="M4 20 V12 A9 9 0 0 1 20 12 V20"/>
|
|
||||||
<line x1="12" y1="5" x2="12" y2="20"/>
|
|
||||||
<line x1="3" y1="20" x2="21" y2="20"/>
|
|
||||||
<line x1="15" y1="12" x2="15" y2="14.5"/>
|
|
||||||
</svg>
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 334 B After Width: | Height: | Size: 316 B |
BIN
website/static/img/brand/social.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
104
website/tools/brand-assets.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate the Furtka brand files from the winning logo (Mark M) everywhere
|
||||||
|
it ships: website, live installer, on-box UI, ISO boot splash, repo docs.
|
||||||
|
|
||||||
|
python3 website/tools/brand-assets.py
|
||||||
|
|
||||||
|
Reuses the geometry and rasteriser of logo-assets.py (the poll showcase), so
|
||||||
|
the mark is drawn from one source. Needs the same build-only deps: cairosvg,
|
||||||
|
Pillow, fontTools. Output files are committed; re-run after a geometry change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
REPO = os.path.abspath(os.path.join(HERE, "..", ".."))
|
||||||
|
|
||||||
|
_spec = importlib.util.spec_from_file_location("logo_assets", os.path.join(HERE, "logo-assets.py"))
|
||||||
|
la = importlib.util.module_from_spec(_spec)
|
||||||
|
_spec.loader.exec_module(la)
|
||||||
|
|
||||||
|
K = "m"
|
||||||
|
COLOR = la.MARKS[K]["color"] # light-theme accent
|
||||||
|
COLOR_DARK = "#ff6b56" # dark-theme accent used by the site and the box UI
|
||||||
|
INK = la.MARKS[K]["ink"]
|
||||||
|
|
||||||
|
|
||||||
|
def out(*parts):
|
||||||
|
path = os.path.join(REPO, *parts)
|
||||||
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def favicon_svg():
|
||||||
|
"""Small-size tier, accent colour, lighter accent on dark browser chrome."""
|
||||||
|
inner = la.body(K, "min", "currentColor")
|
||||||
|
x, y, w, h = la.MARKS[K]["vb"]
|
||||||
|
return (
|
||||||
|
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{x} {y} {w} {h}">'
|
||||||
|
f"<style>svg{{color:{COLOR}}}"
|
||||||
|
f"@media (prefers-color-scheme:dark){{svg{{color:{COLOR_DARK}}}}}</style>"
|
||||||
|
f"{inner}</svg>\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def mark_svg(tier, color):
|
||||||
|
return (
|
||||||
|
la.svg(K, tier, color, standalone=True)
|
||||||
|
.replace(' class="pm"', "")
|
||||||
|
.replace(' aria-hidden="true" focusable="false"', "")
|
||||||
|
+ "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def lockup_svg(color):
|
||||||
|
s = la.lockup(K).replace("currentColor", color)
|
||||||
|
return (
|
||||||
|
s.replace('<svg class="pm"', f"<svg {la.XMLNS}").replace(
|
||||||
|
' aria-hidden="true" focusable="false"', ' role="img" aria-label="Furtka"'
|
||||||
|
)
|
||||||
|
+ "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def write(path, text):
|
||||||
|
with open(path, "w") as f:
|
||||||
|
f.write(text)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
fav = favicon_svg()
|
||||||
|
for p in (
|
||||||
|
("website", "static", "favicon.svg"),
|
||||||
|
("webinstaller", "static", "favicon.svg"),
|
||||||
|
("assets", "www", "favicon.svg"),
|
||||||
|
):
|
||||||
|
write(out(*p), fav)
|
||||||
|
|
||||||
|
la.tile(K, 180, pad=0.2).convert("RGB").save(
|
||||||
|
out("website", "static", "apple-touch-icon.png"), optimize=True
|
||||||
|
)
|
||||||
|
# Default share card for pages without their own og:image.
|
||||||
|
la.lockup_png(K, 1200, 630, False, 230).convert("RGB").save(
|
||||||
|
out("website", "static", "img", "brand", "social.png"), optimize=True
|
||||||
|
)
|
||||||
|
# syslinux (BIOS boot menu) wants a 640x480 PNG and draws its menu over
|
||||||
|
# the middle of it, so the lockup sits small in the top band — same
|
||||||
|
# layout as releng's Arch splash, which build.sh replaces with this.
|
||||||
|
splash_band = la.lockup_png(K, 640, 120, False, 44)
|
||||||
|
splash = la.Image.new("RGBA", (640, 480), INK)
|
||||||
|
splash.alpha_composite(splash_band, (0, 8))
|
||||||
|
splash.convert("RGB").save(out("iso", "overlay", "splash.png"), optimize=True)
|
||||||
|
|
||||||
|
write(out("docs", "brand", "furtka-mark.svg"), mark_svg("flat", COLOR))
|
||||||
|
write(out("docs", "brand", "furtka-mark-small.svg"), mark_svg("min", COLOR))
|
||||||
|
write(out("docs", "brand", "furtka-lockup.svg"), lockup_svg(COLOR))
|
||||||
|
write(out("docs", "brand", "furtka-lockup-white.svg"), lockup_svg("#ffffff"))
|
||||||
|
write(out("docs", "brand", "console-banner.txt"), la.banner(K, cols=20) + "\n")
|
||||||
|
print("brand assets ok")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||