diff --git a/CHANGELOG.md b/CHANGELOG.md index cb9796d..d3e88ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, r ## [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-.iso.sha256` and refresh the site. + ### Security - **App names are validated everywhere.** `POST /api/apps/remove` with diff --git a/README.md b/README.md index 9f6aee0..62d6838 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +

Furtka

+ # Furtka **Open-source home server OS — simple enough for everyone.** · [furtka.org](https://furtka.org) diff --git a/assets/bin/furtka-welcome b/assets/bin/furtka-welcome index 2ddd3d6..8014688 100755 --- a/assets/bin/furtka-welcome +++ b/assets/bin/furtka-welcome @@ -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) new=$( + # Furtka mark (docs/brand/console-banner.txt, from brand-assets.py) + echo ' ▄▄▄▄▄▄▄▄' + echo ' ▄██████████▄' + echo ' █████▀▀▀▀▀▀███' + echo ' ████▀ ████' + echo ' ████ █████████' + echo ' ████ ████' + echo ' ████ ▄▄▄▄▄████' + echo ' ████ █████████' + echo ' ████ █████████' + echo ' ▀▀▀▀ ▀▀▀▀▀▀▀▀▀' echo echo " Furtka is ready." echo diff --git a/assets/www/favicon.svg b/assets/www/favicon.svg new file mode 100644 index 0000000..808cfb5 --- /dev/null +++ b/assets/www/favicon.svg @@ -0,0 +1 @@ + diff --git a/assets/www/https-install/index.html b/assets/www/https-install/index.html index b517977..8d67576 100644 --- a/assets/www/https-install/index.html +++ b/assets/www/https-install/index.html @@ -2,6 +2,7 @@ + Install local HTTPS · Furtka diff --git a/assets/www/index.html b/assets/www/index.html index 86c1559..309e507 100644 --- a/assets/www/index.html +++ b/assets/www/index.html @@ -2,6 +2,7 @@ + Furtka diff --git a/assets/www/settings/index.html b/assets/www/settings/index.html index e403c2e..fb54467 100644 --- a/assets/www/settings/index.html +++ b/assets/www/settings/index.html @@ -2,6 +2,7 @@ + Settings · Furtka diff --git a/assets/www/style.css b/assets/www/style.css index 85fa9d2..381f845 100644 --- a/assets/www/style.css +++ b/assets/www/style.css @@ -73,13 +73,17 @@ body { align-items: center; 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 { content: ""; - width: 0.7rem; - height: 0.7rem; - background: var(--accent); - border-radius: 2px; - transform: rotate(45deg); + width: 1.35rem; + height: 1.35rem; + /* Brand red, not the UI's green accent — the logo keeps its colour. + This stylesheet is dark-first; the light theme gets the deeper red. */ + 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 a { @@ -490,3 +494,8 @@ details.log-details[open] > summary { color: var(--fg); } color: var(--accent); } .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; } +} diff --git a/docs/brand/README.md b/docs/brand/README.md new file mode 100644 index 0000000..2d549ec --- /dev/null +++ b/docs/brand/README.md @@ -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. diff --git a/docs/brand/console-banner.txt b/docs/brand/console-banner.txt new file mode 100644 index 0000000..7445b03 --- /dev/null +++ b/docs/brand/console-banner.txt @@ -0,0 +1,10 @@ + ▄▄▄▄▄▄▄▄ + ▄██████████▄ + █████▀▀▀▀▀▀███ + ████▀ ████ + ████ █████████ + ████ ████ + ████ ▄▄▄▄▄████ + ████ █████████ + ████ █████████ + ▀▀▀▀ ▀▀▀▀▀▀▀▀▀ diff --git a/docs/brand/furtka-lockup-white.svg b/docs/brand/furtka-lockup-white.svg new file mode 100644 index 0000000..a494578 --- /dev/null +++ b/docs/brand/furtka-lockup-white.svg @@ -0,0 +1 @@ + diff --git a/docs/brand/furtka-lockup.svg b/docs/brand/furtka-lockup.svg new file mode 100644 index 0000000..2e3bbb0 --- /dev/null +++ b/docs/brand/furtka-lockup.svg @@ -0,0 +1 @@ + diff --git a/docs/brand/furtka-mark-small.svg b/docs/brand/furtka-mark-small.svg new file mode 100644 index 0000000..e498944 --- /dev/null +++ b/docs/brand/furtka-mark-small.svg @@ -0,0 +1 @@ + diff --git a/docs/brand/furtka-mark.svg b/docs/brand/furtka-mark.svg new file mode 100644 index 0000000..055c6d5 --- /dev/null +++ b/docs/brand/furtka-mark.svg @@ -0,0 +1 @@ + diff --git a/furtka/api.py b/furtka/api.py index c5831cd..76c78b8 100644 --- a/furtka/api.py +++ b/furtka/api.py @@ -73,6 +73,7 @@ _HTML = """ + Furtka Apps @@ -557,12 +558,14 @@ _HTML_LOGIN = """ + Furtka · {{ TITLE }}
+

{{ HEADING }}

{{ LEDE }}

diff --git a/iso/build.sh b/iso/build.sh index 1587633..d7cb387 100755 --- a/iso/build.sh +++ b/iso/build.sh @@ -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" 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 # bootloader configs (BIOS syslinux, GRUB, systemd-boot for UEFI). Rewrite to # our brand. Done with sed (not a static overlay) so upstream archiso file diff --git a/iso/overlay/airootfs/usr/local/bin/furtka-update-issue b/iso/overlay/airootfs/usr/local/bin/furtka-update-issue index e56b1c5..ba62c44 100755 --- a/iso/overlay/airootfs/usr/local/bin/furtka-update-issue +++ b/iso/overlay/airootfs/usr/local/bin/furtka-update-issue @@ -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) new=$( + # Furtka mark (docs/brand/console-banner.txt, from brand-assets.py) + echo ' ▄▄▄▄▄▄▄▄' + echo ' ▄██████████▄' + echo ' █████▀▀▀▀▀▀███' + echo ' ████▀ ████' + echo ' ████ █████████' + echo ' ████ ████' + echo ' ████ ▄▄▄▄▄████' + echo ' ████ █████████' + echo ' ████ █████████' + echo ' ▀▀▀▀ ▀▀▀▀▀▀▀▀▀' echo echo " Open Furtka in a browser on another device on your network:" echo diff --git a/iso/overlay/splash.png b/iso/overlay/splash.png new file mode 100644 index 0000000..d65bb9b Binary files /dev/null and b/iso/overlay/splash.png differ diff --git a/webinstaller/static/favicon.svg b/webinstaller/static/favicon.svg new file mode 100644 index 0000000..808cfb5 --- /dev/null +++ b/webinstaller/static/favicon.svg @@ -0,0 +1 @@ + diff --git a/webinstaller/templates/base.html b/webinstaller/templates/base.html index e90efb5..f6b7ab4 100644 --- a/webinstaller/templates/base.html +++ b/webinstaller/templates/base.html @@ -4,6 +4,7 @@ {% block title %}Furtka Installer{% endblock %} + {% block head_extra %}{% endblock %} @@ -11,12 +12,8 @@ diff --git a/website/layouts/partials/gate.html b/website/layouts/partials/gate.html index 2a5036b..dabcedb 100644 --- a/website/layouts/partials/gate.html +++ b/website/layouts/partials/gate.html @@ -1,6 +1,2 @@ - +{{- /* Furtka mark (M, small-size tier) — generated by tools/brand-assets.py from logo-assets.py geometry. */ -}} + diff --git a/website/layouts/partials/head.html b/website/layouts/partials/head.html index f32076a..521d87a 100644 --- a/website/layouts/partials/head.html +++ b/website/layouts/partials/head.html @@ -10,12 +10,13 @@ + -{{ with .Params.image }} +{{ with .Params.image | default "/img/brand/social.png" }} {{ end }} diff --git a/website/layouts/poll/single.html b/website/layouts/poll/single.html index cf83d34..2d8a90e 100644 --- a/website/layouts/poll/single.html +++ b/website/layouts/poll/single.html @@ -4,32 +4,43 @@ side gets the "first thing on the page" advantage for the whole poll. */}} {{- $order := $p.order | default (slice "m" "r") -}} {{- $s := $p.show -}} -
+
+ {{ with $p.result }}

{{ . | markdownify }}

{{ end }}
{{ .Content }}
{{/* ── Quick vote ─────────────────────────────────────────── */}}
{{ range $k := $order }}{{ $d := index site.Data.logo $k }} {{ $c := index $p $k }} -
+ {{- $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")) }} +
{{ $d.display | safeHTML }}

{{ $c.title }}

{{ $c.text }}

+ {{ if $p.closed }} + {{ $n := index $p.final $k }} + +

{{ $n }} / {{ $total }}

+ {{ else }}

+ {{ end }}
{{ end }}
+ {{ if not $p.closed }}

{{ $p.labels.nojs }}

+ {{ end }} {{/* ── Showcase ───────────────────────────────────────────── */}}
@@ -148,6 +159,7 @@
{{/* ── Vote again at the end ─────────────────────────────── */}} + {{ if not $p.closed }}

{{ $s.final }}

@@ -162,7 +174,10 @@

{{ $p.labels.note | safeHTML }}

+ {{ end }}
+{{ if not $p.closed }} {{ $js := resources.Get "js/poll.js" | minify | fingerprint }} {{ end }} +{{ end }} diff --git a/website/static/apple-touch-icon.png b/website/static/apple-touch-icon.png new file mode 100644 index 0000000..23d0821 Binary files /dev/null and b/website/static/apple-touch-icon.png differ diff --git a/website/static/favicon.svg b/website/static/favicon.svg index 9898a02..808cfb5 100644 --- a/website/static/favicon.svg +++ b/website/static/favicon.svg @@ -1,6 +1 @@ - - - - - - + diff --git a/website/static/img/brand/social.png b/website/static/img/brand/social.png new file mode 100644 index 0000000..89ec678 Binary files /dev/null and b/website/static/img/brand/social.png differ diff --git a/website/tools/brand-assets.py b/website/tools/brand-assets.py new file mode 100644 index 0000000..a509f3d --- /dev/null +++ b/website/tools/brand-assets.py @@ -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'' + f"" + f"{inner}\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('