2026-10-28 10:42:47 +01:00
|
|
|
#!/usr/bin/env python3
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
"""Makes the apps' icons, one family (ADR 0031): each app's Lucide glyph on a white
|
|
|
|
|
square (TILE; D10, back from D8's dark one), fading from cobalt through the colours
|
|
|
|
|
APPS names, top left to bottom right across Lucide's 24-unit box, the stops evenly
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
spaced, at Lucide's stroke width (2), or, for Halkyon (Learn), the kingfisher in its
|
|
|
|
|
own colours on the same square (ADR 0045, D14; ADR 0031, D12),
|
2026-10-28 10:42:47 +01:00
|
|
|
1024 x 1024 pixels (<app>/<flutter package>/assets/icon/<icon>); the launchers and
|
|
|
|
|
the launch screens round its corners themselves. Then, for each app:
|
|
|
|
|
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
- Lucide's licence (ISC) beside a glyph's icon (assets/icon/LICENSE-lucide.txt),
|
|
|
|
|
which the app registers in its licence list;
|
2026-10-28 10:42:47 +01:00
|
|
|
- the snap's icon where the app has a snap (Learn's snap/gui/halkyon-learn.png at the
|
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.
apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 15:35:40 +01:00
|
|
|
repository's root, Todo's apps/todo/snap/gui/halkyon-todo.png; 512 x 512 pixels, the
|
2026-11-07 23:19:19 +01:00
|
|
|
Snap Store's size), the icon with rounded corners, as other apps' tiles
|
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.
apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 15:35:40 +01:00
|
|
|
have (radius 142.222 of 512, about 28 %);
|
2026-10-28 10:42:47 +01:00
|
|
|
- through flutter_launcher_icons, configured in the app's pubspec: Android's adaptive
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
icon with its monochrome layer, which themed icons tint (D9), and its older square
|
|
|
|
|
one, iOS's with its dark and tinted variants (D9), macOS's (the rounded tile on
|
|
|
|
|
Apple's grid, 824 of 1024 pixels) and Windows' (the rounded tile), from the images
|
|
|
|
|
derived in the app's build/icons/, which git ignores;
|
2026-10-28 10:42:47 +01:00
|
|
|
- Windows' .ico once more, with every size Windows shows (16 to 256 pixels): the tool
|
|
|
|
|
writes only the 256 one and leaves Windows to scale it down for its small views;
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
- the kingfisher for the app's launch screen
|
|
|
|
|
(assets/icon/halkyon_<app>_kingfisher.png, ADR 0031, D11): the commissioned drawing
|
|
|
|
|
(tool/kingfisher.svg, unchanged), on transparency, cut to the bird; Halkyon's in
|
|
|
|
|
its own colours (D12), the others' with its warm parts, orange and rust, in the
|
|
|
|
|
last two of the app's colours and its accents in the second last, lifted, its
|
|
|
|
|
cobalt every app's;
|
2026-10-28 10:42:47 +01:00
|
|
|
- the iOS project file as it was: the tool sets every build setting with
|
|
|
|
|
ASSETCATALOG in its name to the icon set's name, Xcode's yes-or-no
|
|
|
|
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS too, which breaks the
|
|
|
|
|
project (flutter_launcher_icons issue 634); the project names its icon set already.
|
|
|
|
|
|
|
|
|
|
The glyphs come from lucide-static at a pinned release (LUCIDE below), fetched from
|
|
|
|
|
jsDelivr, and are drawn by librsvg, as they have arcs and curves (bike's wheels).
|
|
|
|
|
|
|
|
|
|
Run from the repository's root with Python 3, Pillow, PyGObject with librsvg and
|
|
|
|
|
pycairo (Ubuntu's python3-pil, python3-gi, gir1.2-rsvg-2.0, python3-cairo), the apps'
|
|
|
|
|
dependencies fetched, naming the apps to draw, or none for all of them:
|
|
|
|
|
|
|
|
|
|
python3 -I tool/app_icons.py train
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
import urllib.request
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from io import BytesIO
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import cairo
|
|
|
|
|
import gi
|
|
|
|
|
|
|
|
|
|
gi.require_version("Rsvg", "2.0")
|
|
|
|
|
from gi.repository import GLib, Rsvg # noqa: E402 (after the version is required)
|
|
|
|
|
from PIL import Image, ImageDraw # noqa: E402
|
|
|
|
|
|
|
|
|
|
LUCIDE = "1.50.0"
|
|
|
|
|
CDN = f"https://cdn.jsdelivr.net/npm/lucide-static@{LUCIDE}"
|
|
|
|
|
|
|
|
|
|
COBALT = "#3C57E5"
|
|
|
|
|
VIOLET = "#8E4FD6"
|
|
|
|
|
STRAWBERRY = "#EC3E7C"
|
|
|
|
|
CARROT = "#F78400"
|
|
|
|
|
PACIFIC = "#179FD9"
|
|
|
|
|
FERN = "#3CBB3A"
|
|
|
|
|
REEF = "#1DA583"
|
2026-11-02 23:09:38 +01:00
|
|
|
ENZIAN = "#5252CC"
|
|
|
|
|
PURPLE = "#8080FF"
|
2026-10-28 10:42:47 +01:00
|
|
|
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
# ADR 0031, D10: the tile is white again (D1), the gradient as APPS gives it. iOS's
|
|
|
|
|
# dark icon (D9) sits on iOS's own dark background, so there every stop is mixed this
|
|
|
|
|
# share towards white (D8's lift), keeping cobalt above 4.5 : 1 against it.
|
|
|
|
|
TILE = "#FFFFFF"
|
|
|
|
|
DARK_LIFT = 0.3
|
2026-11-07 23:19:19 +01:00
|
|
|
|
2026-10-28 10:42:47 +01:00
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class AppIcon:
|
|
|
|
|
"""An app's icon: where it goes, its glyph and its gradient's colours."""
|
|
|
|
|
|
|
|
|
|
folder: str
|
|
|
|
|
package: str
|
|
|
|
|
icon: str
|
|
|
|
|
glyph: str
|
|
|
|
|
stops: tuple[str, ...]
|
|
|
|
|
snap: str | None = None
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
# Its icon is the kingfisher in its own colours, not the glyph (ADR 0045, D14).
|
|
|
|
|
bird: bool = False
|
2026-10-28 10:42:47 +01:00
|
|
|
|
|
|
|
|
|
feat(meditate): the app: Today, Explore, Profile, the sitting and its bells
halkyon_meditate_flutter on halkyon_app and halkyon_ui (ADR 0038): the
first start, sign-in and account as every Halkyon app's, a bar below
medium windows and a rail with the icon from medium on, fern washes,
Settings in cards (Sync, the operator's, Sitting, Practice, the
account, What's new, About, the version), English and German.
- Today: a greeting by the hour, Sit now at 5, 10, 20 minutes and the
timer's own length, the practice of the day, the week with the days
in a row, favourites, recently sat (D9).
- Explore: six practices of Halkyon's own, written for the app, found
by a search without case or accents, by goal and by kind; each page
with how it goes, its lengths, its heart and Start (D4, D10).
- The sitting, over the tabs: its time from the clock, a ring counting
down, the cue of the moment, Pause and End, the bells at the start,
between and at the end, the screen kept on; ended early, kept from a
minute on where the person says so (D5, D6).
- Profile: every Halkyon app's, then the person's practice: minutes,
sittings, days, the week and every sitting kept, to delete (D8).
- Settings and sittings sealed with the person's key and synced (D7).
The bells are synthesised by apps/meditate/tool/bells.py, the icon is
Lucide's sprout from cobalt through pacific into fern
(tool/app_icons.py). Releases and the deploy are a later step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 09:25:30 +01:00
|
|
|
# ADR 0031, D5: each from cobalt into its app's backdrop.
|
2026-10-28 10:42:47 +01:00
|
|
|
APPS = {
|
|
|
|
|
"learn": AppIcon(
|
|
|
|
|
"apps/learn",
|
|
|
|
|
"halkyon_learn_flutter",
|
|
|
|
|
"halkyon_learn_icon.png",
|
|
|
|
|
"graduation-cap",
|
2026-11-02 23:09:38 +01:00
|
|
|
(COBALT, ENZIAN, PURPLE),
|
2026-10-28 10:42:47 +01:00
|
|
|
snap="snap/gui/halkyon-learn.png",
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
bird=True,
|
2026-10-28 10:42:47 +01:00
|
|
|
),
|
|
|
|
|
"todo": AppIcon(
|
|
|
|
|
"apps/todo",
|
|
|
|
|
"halkyon_todo_flutter",
|
|
|
|
|
"halkyon_todo_icon.png",
|
|
|
|
|
"list-todo",
|
2026-11-02 23:09:38 +01:00
|
|
|
(COBALT, VIOLET, STRAWBERRY),
|
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.
apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 15:35:40 +01:00
|
|
|
snap="apps/todo/snap/gui/halkyon-todo.png",
|
2026-10-28 10:42:47 +01:00
|
|
|
),
|
|
|
|
|
"train": AppIcon(
|
|
|
|
|
"apps/train",
|
|
|
|
|
"halkyon_train_flutter",
|
|
|
|
|
"halkyon_train_icon.png",
|
2026-11-02 23:09:38 +01:00
|
|
|
"dumbbell",
|
2026-10-28 10:42:47 +01:00
|
|
|
(COBALT, VIOLET, STRAWBERRY, CARROT),
|
2026-11-03 11:48:19 +01:00
|
|
|
snap="apps/train/snap/gui/halkyon-train.png",
|
2026-10-28 10:42:47 +01:00
|
|
|
),
|
feat(meditate): the app: Today, Explore, Profile, the sitting and its bells
halkyon_meditate_flutter on halkyon_app and halkyon_ui (ADR 0038): the
first start, sign-in and account as every Halkyon app's, a bar below
medium windows and a rail with the icon from medium on, fern washes,
Settings in cards (Sync, the operator's, Sitting, Practice, the
account, What's new, About, the version), English and German.
- Today: a greeting by the hour, Sit now at 5, 10, 20 minutes and the
timer's own length, the practice of the day, the week with the days
in a row, favourites, recently sat (D9).
- Explore: six practices of Halkyon's own, written for the app, found
by a search without case or accents, by goal and by kind; each page
with how it goes, its lengths, its heart and Start (D4, D10).
- The sitting, over the tabs: its time from the clock, a ring counting
down, the cue of the moment, Pause and End, the bells at the start,
between and at the end, the screen kept on; ended early, kept from a
minute on where the person says so (D5, D6).
- Profile: every Halkyon app's, then the person's practice: minutes,
sittings, days, the week and every sitting kept, to delete (D8).
- Settings and sittings sealed with the person's key and synced (D7).
The bells are synthesised by apps/meditate/tool/bells.py, the icon is
Lucide's sprout from cobalt through pacific into fern
(tool/app_icons.py). Releases and the deploy are a later step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 09:25:30 +01:00
|
|
|
"meditate": AppIcon(
|
|
|
|
|
"apps/meditate",
|
|
|
|
|
"halkyon_meditate_flutter",
|
|
|
|
|
"halkyon_meditate_icon.png",
|
|
|
|
|
"sprout",
|
|
|
|
|
(COBALT, PACIFIC, FERN),
|
|
|
|
|
),
|
2026-10-28 10:42:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SIZE = 1024
|
|
|
|
|
# The 24-unit box Lucide draws in takes this share of the square, centred: well
|
|
|
|
|
# inside the circle Android's adaptive icons keep (66 %).
|
|
|
|
|
GLYPH = 0.6
|
|
|
|
|
# The corners the launch screen and launchers round to.
|
|
|
|
|
RADIUS = 142.222 / 512
|
|
|
|
|
SUPERSAMPLING = 4
|
|
|
|
|
SNAP_SIZE = 512
|
|
|
|
|
# Apple's grid: the tile is 824 of the icon's 1024 pixels, centred.
|
|
|
|
|
MACOS_CANVAS = 1024
|
|
|
|
|
MACOS_TILE = 824
|
|
|
|
|
WINDOWS_SIZES = [16, 20, 24, 32, 40, 48, 64, 128, 256]
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
KINGFISHER = Path("tool/kingfisher.svg")
|
|
|
|
|
# The launch screen shows it at 160 logical pixels: three times that.
|
|
|
|
|
KINGFISHER_SIZE = 480
|
|
|
|
|
# The square around the bird in the drawing's 512-unit box.
|
|
|
|
|
KINGFISHER_BOX = "86 89 340 340"
|
|
|
|
|
# On an icon the square around the bird takes this share of it, centred, smaller
|
|
|
|
|
# than a glyph's: its beak reaches into the corner, and at this size it stays inside
|
|
|
|
|
# the 66 dp circle of the 108 Android keeps on every launcher's shape (0.306).
|
|
|
|
|
BIRD = 0.47
|
2026-10-28 10:42:47 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch(name: str) -> str:
|
|
|
|
|
with urllib.request.urlopen(f"{CDN}/{name}", timeout=30) as response:
|
|
|
|
|
return response.read().decode("utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rgb(colour: str) -> tuple[float, float, float]:
|
|
|
|
|
return tuple(int(colour[i : i + 2], 16) / 255 for i in (1, 3, 5))
|
|
|
|
|
|
|
|
|
|
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
def lifted(colour: str, lift: float) -> tuple[float, float, float]:
|
|
|
|
|
"""[colour] mixed [lift] towards white."""
|
|
|
|
|
return tuple(c + (1 - c) * lift for c in rgb(colour))
|
2026-11-07 23:19:19 +01:00
|
|
|
|
|
|
|
|
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
def glyph_mask(glyph: str, svg: str) -> cairo.ImageSurface:
|
|
|
|
|
"""[glyph]'s strokes alone, SIZE pixels square, where every icon draws it."""
|
2026-10-28 10:42:47 +01:00
|
|
|
if f"lucide-static v{LUCIDE}" not in svg:
|
|
|
|
|
raise SystemExit(f"Not lucide-static {LUCIDE}'s {glyph}.")
|
|
|
|
|
if 'viewBox="0 0 24 24"' not in svg or 'stroke-width="2"' not in svg:
|
|
|
|
|
raise SystemExit(f"{glyph} isn't drawn in Lucide's 24 units at width 2.")
|
|
|
|
|
handle = Rsvg.Handle.new_from_data(svg.replace("currentColor", "#000").encode())
|
|
|
|
|
box = SIZE * GLYPH
|
|
|
|
|
offset = (SIZE - box) / 2
|
|
|
|
|
mask = cairo.ImageSurface(cairo.FORMAT_ARGB32, SIZE, SIZE)
|
|
|
|
|
viewport = Rsvg.Rectangle()
|
|
|
|
|
viewport.x, viewport.y, viewport.width, viewport.height = offset, offset, box, box
|
|
|
|
|
try:
|
|
|
|
|
handle.render_document(cairo.Context(mask), viewport)
|
|
|
|
|
except GLib.Error as error:
|
|
|
|
|
raise SystemExit(f"librsvg couldn't draw {glyph}: {error.message}")
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
return mask
|
2026-10-28 10:42:47 +01:00
|
|
|
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
|
|
|
|
|
def gradient_of(stops: tuple[str, ...], lift: float = 0) -> cairo.LinearGradient:
|
|
|
|
|
"""The gradient of [stops], each mixed [lift] towards white, across the glyph's
|
|
|
|
|
box, top left to bottom right."""
|
|
|
|
|
box = SIZE * GLYPH
|
|
|
|
|
offset = (SIZE - box) / 2
|
2026-10-28 10:42:47 +01:00
|
|
|
gradient = cairo.LinearGradient(offset, offset, offset + box, offset + box)
|
|
|
|
|
for i, colour in enumerate(stops):
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
gradient.add_color_stop_rgb(i / (len(stops) - 1), *lifted(colour, lift))
|
|
|
|
|
return gradient
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def drawn(
|
|
|
|
|
mask: cairo.ImageSurface,
|
|
|
|
|
source: cairo.Pattern,
|
|
|
|
|
background: tuple[float, float, float] | None,
|
|
|
|
|
) -> Image.Image:
|
|
|
|
|
"""The glyph's [mask] filled with [source], on [background] or, without one,
|
|
|
|
|
on transparency."""
|
|
|
|
|
surface = cairo.ImageSurface(
|
|
|
|
|
cairo.FORMAT_ARGB32 if background is None else cairo.FORMAT_RGB24, SIZE, SIZE
|
|
|
|
|
)
|
|
|
|
|
context = cairo.Context(surface)
|
|
|
|
|
if background is not None:
|
|
|
|
|
context.set_source_rgb(*background)
|
|
|
|
|
context.paint()
|
|
|
|
|
context.set_source(source)
|
2026-10-28 10:42:47 +01:00
|
|
|
context.mask_surface(mask)
|
|
|
|
|
png = BytesIO()
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
surface.write_to_png(png)
|
2026-10-28 10:42:47 +01:00
|
|
|
png.seek(0)
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
return Image.open(png).convert("RGB" if background is not None else "RGBA")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def icon(mask: cairo.ImageSurface, stops: tuple[str, ...]) -> Image.Image:
|
|
|
|
|
"""The glyph's [mask] on TILE in the gradient of [stops]."""
|
|
|
|
|
return drawn(mask, gradient_of(stops), rgb(TILE))
|
|
|
|
|
|
|
|
|
|
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
def themed(
|
|
|
|
|
mask: cairo.ImageSurface, stops: tuple[str, ...], bird: bool = False
|
|
|
|
|
) -> dict[str, Image.Image]:
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
"""The variants the systems colour themselves (ADR 0031, D9), by their file
|
|
|
|
|
names in build/icons/: Android's monochrome layer (the glyph in white on
|
|
|
|
|
transparency, which the launcher tints from the wallpaper), iOS's dark icon
|
|
|
|
|
(the glyph in its gradient, lifted by DARK_LIFT, on transparency, which iOS sets
|
|
|
|
|
on its own dark background) and iOS's tinted one (the glyph in white on black, a grayscale
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
image iOS tints). For the [bird], its outline in white, and on iOS's dark
|
|
|
|
|
background the bird itself (ADR 0045, D14)."""
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
white = cairo.SolidPattern(1, 1, 1)
|
|
|
|
|
return {
|
|
|
|
|
"monochrome.png": drawn(mask, white, None),
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
"ios_dark.png": image_of(mask)
|
|
|
|
|
if bird
|
|
|
|
|
else drawn(mask, gradient_of(stops, DARK_LIFT), None),
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
"ios_tinted.png": drawn(mask, white, (0, 0, 0)),
|
|
|
|
|
}
|
2026-10-28 10:42:47 +01:00
|
|
|
|
|
|
|
|
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
def hex_of(colour: tuple[float, float, float]) -> str:
|
|
|
|
|
return "#" + "".join(f"{round(c * 255):02x}" for c in colour)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def bird_surface(
|
|
|
|
|
stops: tuple[str, ...] | None, size: int, offset: float, box: float
|
|
|
|
|
) -> cairo.ImageSurface:
|
|
|
|
|
"""The kingfisher on transparency, [size] pixels square, the square around the
|
|
|
|
|
bird drawn [box] pixels wide at [offset] from the top left; in the colours of
|
|
|
|
|
[stops] (D11), or in its own without."""
|
|
|
|
|
svg = KINGFISHER.read_text(encoding="utf-8")
|
|
|
|
|
tile = '<rect class="cls-1" x="19" y="19" width="474" height="474" rx="65.47"/>'
|
|
|
|
|
if tile not in svg:
|
|
|
|
|
raise SystemExit(f"{KINGFISHER} isn't the commissioned drawing.")
|
|
|
|
|
# Its own white tile goes: the bird stands on the launch screen, or on TILE.
|
|
|
|
|
svg = svg.replace(tile, "")
|
|
|
|
|
svg = svg.replace('viewBox="0 0 512 512"', f'viewBox="{KINGFISHER_BOX}"')
|
|
|
|
|
if stops is not None:
|
|
|
|
|
end, between = stops[-1], stops[-2]
|
|
|
|
|
for drawn_as, becomes in {
|
|
|
|
|
"#f78400": end,
|
|
|
|
|
"#c44800": between,
|
|
|
|
|
"#5252cc": between,
|
|
|
|
|
"#179fd9": hex_of(lifted(between, DARK_LIFT)),
|
|
|
|
|
}.items():
|
|
|
|
|
svg = svg.replace(drawn_as, becomes.lower())
|
|
|
|
|
handle = Rsvg.Handle.new_from_data(svg.encode())
|
|
|
|
|
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, size, size)
|
|
|
|
|
viewport = Rsvg.Rectangle()
|
|
|
|
|
viewport.x, viewport.y = offset, offset
|
|
|
|
|
viewport.width = viewport.height = box
|
|
|
|
|
try:
|
|
|
|
|
handle.render_document(cairo.Context(surface), viewport)
|
|
|
|
|
except GLib.Error as error:
|
|
|
|
|
raise SystemExit(f"librsvg couldn't draw the kingfisher: {error.message}")
|
|
|
|
|
return surface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def image_of(surface: cairo.ImageSurface) -> Image.Image:
|
|
|
|
|
out = BytesIO()
|
|
|
|
|
surface.write_to_png(out)
|
|
|
|
|
return Image.open(BytesIO(out.getvalue())).convert("RGBA")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def kingfisher(stops: tuple[str, ...] | None) -> Image.Image:
|
|
|
|
|
"""The kingfisher for a launch screen, KINGFISHER_SIZE square, in the colours of
|
|
|
|
|
[stops] (D11), or in its own without (D12)."""
|
|
|
|
|
return image_of(bird_surface(stops, KINGFISHER_SIZE, 0, KINGFISHER_SIZE))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def bird_mask() -> cairo.ImageSurface:
|
|
|
|
|
"""The kingfisher in its own colours, SIZE pixels square, where a glyph stands
|
|
|
|
|
on an icon (ADR 0045, D14): its shape is the themed variants' mask."""
|
|
|
|
|
box = SIZE * BIRD
|
|
|
|
|
return bird_surface(None, SIZE, (SIZE - box) / 2, box)
|
|
|
|
|
|
|
|
|
|
|
2026-10-28 10:42:47 +01:00
|
|
|
def rounded(source: Image.Image, size: int) -> Image.Image:
|
|
|
|
|
"""[source] at [size] pixels, its corners rounded off to transparency."""
|
|
|
|
|
picture = source.convert("RGBA").resize((size, size), Image.LANCZOS)
|
|
|
|
|
large = size * SUPERSAMPLING
|
|
|
|
|
mask = Image.new("L", (large, large), 0)
|
|
|
|
|
ImageDraw.Draw(mask).rounded_rectangle(
|
|
|
|
|
(0, 0, large - 1, large - 1), radius=RADIUS * large, fill=255
|
|
|
|
|
)
|
|
|
|
|
picture.putalpha(mask.resize((size, size), Image.LANCZOS))
|
|
|
|
|
return picture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make(app: AppIcon, license_text: str) -> None:
|
|
|
|
|
package = Path(app.folder) / app.package
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
if app.bird:
|
|
|
|
|
mask = bird_mask()
|
|
|
|
|
source = drawn(mask, cairo.SurfacePattern(mask), rgb(TILE))
|
|
|
|
|
else:
|
|
|
|
|
mask = glyph_mask(app.glyph, fetch(f"icons/{app.glyph}.svg"))
|
|
|
|
|
source = icon(mask, app.stops)
|
2026-10-28 10:42:47 +01:00
|
|
|
path = package / "assets/icon" / app.icon
|
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
source.save(path, optimize=True)
|
|
|
|
|
print(f"Wrote {path}.")
|
|
|
|
|
license_path = package / "assets/icon/LICENSE-lucide.txt"
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
if app.bird:
|
|
|
|
|
license_path.unlink(missing_ok=True)
|
|
|
|
|
else:
|
|
|
|
|
license_path.write_text(license_text, encoding="utf-8")
|
|
|
|
|
print(f"Wrote {license_path}.")
|
|
|
|
|
emblem = package / "assets/icon" / app.icon.replace("_icon.png", "_kingfisher.png")
|
|
|
|
|
kingfisher(None if app.bird else app.stops).save(emblem, optimize=True)
|
|
|
|
|
print(f"Wrote {emblem}.")
|
2026-10-28 10:42:47 +01:00
|
|
|
if app.snap:
|
|
|
|
|
rounded(source, SNAP_SIZE).save(app.snap, optimize=True)
|
|
|
|
|
print(f"Wrote {app.snap}.")
|
|
|
|
|
|
|
|
|
|
derived = package / "build/icons"
|
|
|
|
|
derived.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
rounded(source, 1024).save(derived / "rounded.png")
|
|
|
|
|
macos = Image.new("RGBA", (MACOS_CANVAS, MACOS_CANVAS), (0, 0, 0, 0))
|
|
|
|
|
margin = (MACOS_CANVAS - MACOS_TILE) // 2
|
|
|
|
|
macos.alpha_composite(rounded(source, MACOS_TILE), (margin, margin))
|
|
|
|
|
macos.save(derived / "macos.png")
|
feat(ui,learn,todo,train,meditate): the kingfisher on the launch screens, and as Learn's icon
The commissioned kingfisher (tool/kingfisher.svg) becomes Halkyon's
again. Learn's launcher icon is the bird in its own colours on the
white tile, its themed variants the bird's outline, and its launch
screen shows the bird alone (ADR 0031, D12; ADR 0045, D14). Todo,
Train and Meditate keep their glyphs on the launchers, and their launch
screens show the bird in their own colours (D11). tool/app_icons.py
draws all of them; halkyon_ui's LaunchScreen takes an emblem in place
of the icon. Lucide's notice leaves Learn's licence list, as its icon
no longer is Lucide's.
Whats-New: Halkyon's kingfisher is back: Learn's icon, and on the screen while each app opens.
Whats-New-De: Halkyons Eisvogel ist zurück: als Symbol von Learn und auf dem Bildschirm, während jede App startet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-12 13:33:37 +01:00
|
|
|
for name, picture in themed(mask, app.stops, app.bird).items():
|
feat(learn,train,todo,meditate): the icons on a white tile again, coloured by the system's theme on Android and iOS (ADR 0031, D9, D10)
D10 returns every icon to D1's white tile, D8's dark one undone, the
gradients as D5 gives them (cobalt holds about 5.7 : 1 against white).
D9 adds the variants the systems colour by their theme, all from the same
glyph at the same place, drawn by tool/app_icons.py and written into the
projects by flutter_launcher_icons: Android's monochrome layer, which
themed icons tint (Android 13 and later), and iOS's dark and tinted icons
(iOS 18 and later), the dark one keeping D8's lift of 30 % towards white
on iOS's own dark background. macOS needs an Icon Composer file made on a
Mac and keeps the plain icon until then.
Whats-New: The app's icon is white again, and takes the system's colours where Android or iOS themes its icons.
Whats-New-De: Das App-Symbol ist wieder weiß und nimmt die Farben des Systems an, wo Android oder iOS seine Symbole einfärbt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-09 23:12:13 +01:00
|
|
|
picture.save(derived / name)
|
2026-10-28 10:42:47 +01:00
|
|
|
|
|
|
|
|
ios_project = package / "ios/Runner.xcodeproj/project.pbxproj"
|
|
|
|
|
kept = ios_project.read_bytes()
|
|
|
|
|
subprocess.run(["dart", "run", "flutter_launcher_icons"], cwd=package, check=True)
|
|
|
|
|
ios_project.write_bytes(kept)
|
|
|
|
|
|
|
|
|
|
windows_icon = package / "windows/runner/resources/app_icon.ico"
|
|
|
|
|
rounded(source, 256).save(
|
|
|
|
|
windows_icon, format="ICO", sizes=[(size, size) for size in WINDOWS_SIZES]
|
|
|
|
|
)
|
|
|
|
|
print(f"Wrote {windows_icon} with {len(WINDOWS_SIZES)} sizes.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
|
|
|
names = sys.argv[1:] or list(APPS)
|
|
|
|
|
unknown = [name for name in names if name not in APPS]
|
|
|
|
|
if unknown:
|
|
|
|
|
raise SystemExit(f"No icon for {', '.join(unknown)}: {', '.join(APPS)}.")
|
|
|
|
|
if not Path("tool/app_icons.py").is_file():
|
|
|
|
|
raise SystemExit("Run this from the repository's root.")
|
|
|
|
|
license_text = fetch("LICENSE")
|
|
|
|
|
for name in names:
|
|
|
|
|
make(APPS[name], license_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|