Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ secure-messenger/
app/windows/ CMake, runner, and bitdm.iss for the installer
app/linux/ runner; the release tar.gz comes from .github/workflows/linux.yml
app/web/ shell for bitdm.net/app, built as in deploy/webapp/README.md
app/tool/symbol/ 3 Python scripts that render and distribute the app icon
app/tool/symbol/ the BitDM mark (f1.py) and the script that writes every icon
app/tool/*.py checks for the main.dart split: pure move, name collisions
server/ Python relay + blob store, 5,106 lines; tests 5,568 lines,
206 tests (18.10.2026)
Expand Down
8 changes: 8 additions & 0 deletions secure-messenger/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ Plattenbeschriftungen, 0.12em für Modulbezeichnungen, 0.08em für Zustände.
- Tiefe entsteht durch **Versatz und weiche Streuung**, nie durch farbige Höfe
ohne Versatz.

## Zeichen

Seit 24.10.2026: ein **Anzeigefenster** mit Punktmatrix, darin leuchtet **DM**
im Akzent, die übrigen Punkte sind aus. Es ist dieselbe Welt wie die App: eine
Gerätefront, die nur dort leuchtet, wo sie etwas anzeigt. Bis 32 Pixel gibt es
eine Kleinfassung ohne Fenster und Raster. Vorlage, Maße und alle Ausgaben:
`app/tool/symbol/` (`py tool/symbol/verteilen.py`).

## Grammatik der Frontplatte

Neue Flächen erben diese Sprache:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed secure-messenger/app/icon_bitdm_1024.png
Binary file not shown.
Binary file removed secure-messenger/app/icon_bitdm_klein.png
Binary file not shown.
Binary file removed secure-messenger/app/icon_master_1024.png
Binary file not shown.
91 changes: 0 additions & 91 deletions secure-messenger/app/tool/symbol/01_gross.py

This file was deleted.

68 changes: 0 additions & 68 deletions secure-messenger/app/tool/symbol/02_klein.py

This file was deleted.

43 changes: 0 additions & 43 deletions secure-messenger/app/tool/symbol/03_verteilen.py

This file was deleted.

52 changes: 29 additions & 23 deletions secure-messenger/app/tool/symbol/README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
# Das App-Symbol neu bauen
# Das BitDM-Zeichen bauen

Reihenfolge, sonst fehlt die Vorlage:
Seit 24.10.2026 gilt Entwurf **F1, „Anzeigefenster“**: ein Displayfenster auf
der Gerätefront, darin eine Punktmatrix, und DM leuchtet in Violett. Henrik hat
es aus sechs Entwürfen und sechs Varianten gewählt. Es löst die Sprechblase mit
dem B ab (`65fd2dc`), die schwarz-weiß war und neben dem Favicon ein zweites
Zeichen bildete.

```bash
py tool/symbol/01_gross.py # icon_bitdm_1024.png — Blase + Doto-B
py tool/symbol/02_klein.py # icon_bitdm_klein.png — kraeftiger, volles B
py tool/symbol/03_verteilen.py # Android, Web, Webseite
py tool/symbol/verteilen.py
```

Danach die `.ico` gemischt schreiben — klein bis 32, Punktmatrix ab 48:
schreibt alles neu, aus einer einzigen Vorlage in `f1.py`:

```python
gr = Image.open('icon_bitdm_1024.png'); kl = Image.open('icon_bitdm_klein.png')
t = [kl.resize((n, n)) for n in (16, 24, 32)] + \
[gr.resize((n, n)) for n in (48, 64, 128, 256)]
t[-1].save('windows/runner/resources/app_icon.ico',
sizes=[x.size for x in t], append_images=t[:-1])
```
| Ziel | Fassung |
|---|---|
| Android `mipmap-*/ic_launcher.png` (48–192) | groß |
| Windows `app_icon.ico` | 16–32 klein, 48–256 groß |
| Web-App `web/favicon.png` (32) | klein |
| Web-App `web/icons/Icon-*.png`, maskierbar | groß |
| F-Droid `fastlane/.../images/icon.png` (512) | groß |
| Website `bilder/icon-180.png`, `bilder/bitdm-zeichen.svg` | groß |
| Website `favicon.ico` | 16/32 klein, 48 groß |
| Website `favicon.svg` | klein |

**Warum zwei Fassungen.** Gemessen an einer Groessenprobe: das Doto-B ist ab
48 Pixeln lesbar, darunter verschmelzen die Punkte, und der Umriss wird bei 16
Pixeln 0,75 Pixel dick — also grau statt weiss. Eine `.ico` darf pro Groesse
ein eigenes Bild tragen, also traegt sie klein die kraeftige Fassung.
**Warum zwei Fassungen.** Unter 48 Pixeln werden Fensterrahmen und
unbeleuchtete Punkte zu grauem Brei, bei 16 Pixeln wäre ein Punkt 0,7 Pixel
breit. Die Kleinfassung zeigt dasselbe DM ohne Fenster und Raster, mit Punkten,
die fast aneinanderstoßen. So bleibt es bei 16 Pixeln eine lesbare Pixelschrift.
Dieselbe Grenze galt schon für das alte Zeichen; damals gemessen an einer
Größenprobe, jetzt erneut nachgesehen.

**Was NICHT nochmal probiert werden muss** (alles am 30.07.2026 gebaut und
verworfen): den gefalteten Rhombus der ersten Vorlage nachzuzeichnen — die
Facetten werden bei 48 Pixeln Brei; Kreise mit Rechteck-Schnitt fuer die Drei —
das beisst ein quadratisches Loch in die Form; `textbbox` zum Zentrieren von
Doto — die Schrift meldet ihre Glyphenmasse ungenau, das B sass 19 Pixel zu
weit links. Zentriert wird nach `getbbox()` der gezeichneten Tinte.
**Maße.** Alles steht im 108er Raster eines Android-Symbols: Kachelradius 24,
Fenster 10/22 bis 98/86 mit Radius 10, Raster 13 × 9 Punkte im Abstand 6,4. DM
sitzt eine Zelle eingerückt auf demselben Raster, die leuchtenden Punkte liegen
also genau über unbeleuchteten. Farben aus `DESIGN.md`: Grund `#0b0c12`, Linie
`#3f424d`, Akzent `#b5abfc`, aus `#1f1d31`.
101 changes: 101 additions & 0 deletions secure-messenger/app/tool/symbol/f1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
"""Das BitDM-Zeichen seit 24.10.2026: Entwurf F1, "Anzeigefenster".

Ein Displayfenster auf der Geraetefront, darin eine Punktmatrix. Die meisten
Punkte sind aus, DM leuchtet. Henrik hat es aus sechs Entwuerfen gewaehlt; es
loest die Sprechblase mit dem B ab (65fd2dc), die schwarz-weiss war, das
Violett der App nicht kannte und neben dem Favicon (fuenf Punkte) ein zweites
Zeichen war.

ALLES STEHT IM 108er RASTER, dem Mass eines Android-Symbols. Daraus rechnen
Pixelbilder und SVG dieselbe Form, damit es nur EINE Vorlage gibt.

ZWEI FASSUNGEN, wie schon beim alten Zeichen gemessen: unter 48 Pixeln werden
Fensterrahmen (1,5 Einheiten) und die unbeleuchteten Punkte zu grauem Brei, bei
16 Pixeln waere ein Punkt 0,7 Pixel breit. Darum bis 32 Pixel `klein`: dasselbe
DM, ohne Fenster und Raster, die Punkte so gross, dass sie fast aneinander
stossen. Das bleibt bei 16 Pixeln ein lesbares DM in Pixelschrift.
"""
from PIL import Image, ImageDraw

GRUND = (11, 12, 18, 255) # #0b0c12, Seitenhintergrund (DESIGN.md)
LINIE = (63, 66, 77, 255) # #3f424d, Hairline/Bezelkante
AUS = (31, 29, 49, 255) # #1f1d31, unbeleuchteter Punkt
AN = (181, 171, 252, 255) # #b5abfc, Akzent: die Anzeige leuchtet

D = ['xxxx.', 'x...x', 'x...x', 'x...x', 'x...x', 'x...x', 'xxxx.']
M = ['x...x', 'xx.xx', 'x.x.x', 'x.x.x', 'x...x', 'x...x', 'x...x']
DM = [d + '.' + m for d, m in zip(D, M)] # 11 Spalten, 7 Zeilen

KACHEL_RADIUS = 24
# gross: Fenster, 13 x 9 Raster, DM eine Zelle eingerueckt
FENSTER = (10, 22, 98, 86)
FENSTER_RADIUS, FENSTER_STRICH = 10, 1.5
RASTER = dict(abstand=6.4, x0=15.6, y0=28.4, spalten=13, zeilen=9, r_aus=2.2, r_an=2.4)
# klein: nur DM, gross gesetzt
KLEIN = dict(abstand=8.2, x0=13.0, y0=29.4, r=3.6)


def _hex(farbe):
return '#%02x%02x%02x' % farbe[:3]


def _punkte(zeilen, abstand, x0, y0):
"""Mittelpunkte der leuchtenden Punkte in Rastereinheiten."""
for y, zeile in enumerate(zeilen):
for x, c in enumerate(zeile):
if c == 'x':
yield x0 + x * abstand, y0 + y * abstand


def zeichne(n, klein=False, ueberabtastung=4):
"""Das Zeichen als RGBA-Bild n x n, mit durchsichtigen Ecken."""
s = n * ueberabtastung
k = s / 108
bild = Image.new('RGBA', (s, s), (0, 0, 0, 0))
d = ImageDraw.Draw(bild)
d.rounded_rectangle((0, 0, s - 1, s - 1), radius=round(KACHEL_RADIUS * k), fill=GRUND)

def kreis(cx, cy, r, farbe):
d.ellipse(((cx - r) * k, (cy - r) * k, (cx + r) * k, (cy + r) * k), fill=farbe)

if klein:
for cx, cy in _punkte(DM, KLEIN['abstand'], KLEIN['x0'], KLEIN['y0']):
kreis(cx, cy, KLEIN['r'], AN)
else:
x0, y0, x1, y1 = FENSTER
d.rounded_rectangle((x0 * k, y0 * k, x1 * k, y1 * k), radius=round(FENSTER_RADIUS * k),
outline=LINIE, width=max(1, round(FENSTER_STRICH * k)))
a = RASTER['abstand']
for y in range(RASTER['zeilen']):
for x in range(RASTER['spalten']):
kreis(RASTER['x0'] + x * a, RASTER['y0'] + y * a, RASTER['r_aus'], AUS)
# DM sitzt eine Zelle eingerueckt auf demselben Raster: die leuchtenden
# Punkte liegen genau ueber unbeleuchteten.
for cx, cy in _punkte(DM, a, RASTER['x0'] + a, RASTER['y0'] + a):
kreis(cx, cy, RASTER['r_an'], AN)
return bild.resize((n, n), Image.LANCZOS)


def svg(klein=False):
"""Dasselbe Zeichen als SVG im 108er Raster."""
teile = [f'<rect width="108" height="108" rx="{KACHEL_RADIUS}" fill="{_hex(GRUND)}"/>']
if klein:
teile += [f'<circle cx="{cx:g}" cy="{cy:g}" r="{KLEIN["r"]:g}"/>'
for cx, cy in _punkte(DM, KLEIN['abstand'], KLEIN['x0'], KLEIN['y0'])]
punkte = f'<g fill="{_hex(AN)}">' + ''.join(teile[1:]) + '</g>'
teile = teile[:1] + [punkte]
else:
x0, y0, x1, y1 = FENSTER
teile.append(f'<rect x="{x0}" y="{y0}" width="{x1 - x0}" height="{y1 - y0}" '
f'rx="{FENSTER_RADIUS}" fill="none" stroke="{_hex(LINIE)}" '
f'stroke-width="{FENSTER_STRICH}"/>')
a = RASTER['abstand']
aus = ''.join(f'<circle cx="{RASTER["x0"] + x * a:.1f}" cy="{RASTER["y0"] + y * a:.1f}" '
f'r="{RASTER["r_aus"]}"/>'
for y in range(RASTER['zeilen']) for x in range(RASTER['spalten']))
an = ''.join(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{RASTER["r_an"]}"/>'
for cx, cy in _punkte(DM, a, RASTER['x0'] + a, RASTER['y0'] + a))
teile += [f'<g fill="{_hex(AUS)}">{aus}</g>', f'<g fill="{_hex(AN)}">{an}</g>']
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 108">'
+ ''.join(teile) + '</svg>\n')
Loading
Loading