fix modifier key order, add EU badge template icons, rename build scripts

- fix modifier key order to Apple canonical: Option+Shift (not Shift+Option)
  across parser, validator, PDF generator, website keyboard viewer, README
- add EU badge template icons for v1.2/v1.3/v1.4 matching Apple's built-in
  keyboard layout icon style (edge-to-edge rounded square, text knockout)
- add build-icons.sh to generate .icns from SVG source via rsvg-convert
- rename create-dmg.sh → build-dmg.sh, update CI workflows
- add website feature icons (install, pdf, versions)
- update website icon to star-on-key design

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 11:01:56 +01:00
parent 1b9ce5dd86
commit 92422e1bf1
26 changed files with 412 additions and 772 deletions

View File

@@ -32,6 +32,9 @@ echo "Building ${BUNDLE_NAME} ${VERSION}"
echo "Bundle: ${BUNDLE_DIR}"
echo
# --- generate icons from SVG sources ---
bash "${SCRIPT_DIR}/build-icons.sh"
# --- assemble bundle from src/ ---
rm -rf "${BUNDLE_DIR}"
mkdir -p "${RESOURCES_DIR}"

View File

@@ -26,11 +26,8 @@ STAGING_DIR="${BUILD_DIR}/dmg-staging"
echo "Creating DMG: ${DMG_NAME}"
# --- auto-build bundle if missing ---
if [[ ! -f "${BUNDLE_DIR}/Contents/Info.plist" ]]; then
echo "Bundle not found, building..."
bash "${SCRIPT_DIR}/build-bundle.sh" --version "${VERSION}"
fi
# --- build bundle (includes validation) ---
bash "${SCRIPT_DIR}/build-bundle.sh" --version "${VERSION}"
# --- auto-build PDFs ---
echo "Building PDFs..."

66
scripts/build-icons.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Generate .icns icon files from SVG sources.
#
# Requires: rsvg-convert (librsvg), iconutil (macOS built-in)
#
# The v1.2/v1.3/v1.4 icon is a template badge with "EU" text.
# The v2.0 icon is a monochrome star ring (managed separately).
#
# Usage: bash scripts/build-icons.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
ICON_DIR="${PROJECT_DIR}/src/icons"
SVG_DIR="${PROJECT_DIR}/EurKEY-macOS-icon/drafts"
BADGE_SVG="${SVG_DIR}/badge-eu-template.svg"
if ! command -v rsvg-convert &> /dev/null; then
echo "SKIP: rsvg-convert not found (install librsvg for icon generation)"
echo "Using existing .icns files from src/icons/"
exit 0
fi
if [[ ! -f "${BADGE_SVG}" ]]; then
echo "ERROR: ${BADGE_SVG} not found"
exit 1
fi
ICONSET="$(mktemp -d)/badge-eu.iconset"
mkdir -p "${ICONSET}"
echo "Generating EU badge icon..."
# render at all required sizes
for size in 16 32 64 128 256 512 1024; do
rsvg-convert -w "${size}" -h "${size}" "${BADGE_SVG}" -o "${ICONSET}/tmp_${size}.png"
done
# map to iconset naming convention
cp "${ICONSET}/tmp_16.png" "${ICONSET}/icon_16x16.png"
cp "${ICONSET}/tmp_32.png" "${ICONSET}/icon_16x16@2x.png"
cp "${ICONSET}/tmp_32.png" "${ICONSET}/icon_32x32.png"
cp "${ICONSET}/tmp_64.png" "${ICONSET}/icon_32x32@2x.png"
cp "${ICONSET}/tmp_128.png" "${ICONSET}/icon_128x128.png"
cp "${ICONSET}/tmp_256.png" "${ICONSET}/icon_128x128@2x.png"
cp "${ICONSET}/tmp_256.png" "${ICONSET}/icon_256x256.png"
cp "${ICONSET}/tmp_512.png" "${ICONSET}/icon_256x256@2x.png"
cp "${ICONSET}/tmp_512.png" "${ICONSET}/icon_512x512.png"
cp "${ICONSET}/tmp_1024.png" "${ICONSET}/icon_512x512@2x.png"
rm "${ICONSET}"/tmp_*.png
# convert to .icns
ICNS_PATH="${ICON_DIR}/badge-eu.icns"
iconutil --convert icns --output "${ICNS_PATH}" "${ICONSET}"
# install for v1.2, v1.3, v1.4 (v2.0 keeps its own icon)
cp "${ICNS_PATH}" "${ICON_DIR}/EurKEY v1.2.icns"
cp "${ICNS_PATH}" "${ICON_DIR}/EurKEY v1.3.icns"
cp "${ICNS_PATH}" "${ICON_DIR}/EurKEY v1.4.icns"
rm "${ICNS_PATH}"
# clean up
rm -rf "$(dirname "${ICONSET}")"
echo "Icons generated for v1.2, v1.3, v1.4"

View File

@@ -2,7 +2,7 @@
"""Generate keyboard layout PDFs from .keylayout files.
Renders an ISO keyboard diagram showing Base, Shift, Option, and
Shift+Option layers on each key, plus dead key composition tables.
Option+Shift layers on each key, plus dead key composition tables.
Requires: fpdf2 (pip install fpdf2)
@@ -72,12 +72,12 @@ KEYBOARD_ROWS = [
MOD_BASE = "0"
MOD_SHIFT = "1"
MOD_OPTION = "3"
MOD_SHIFT_OPTION = "4"
MOD_OPTION_SHIFT = "4"
# Display layers: (modifier_index, color_rgb, position)
DISPLAY_LAYERS = [
(MOD_SHIFT, (0, 40, 170), "top_left"),
(MOD_SHIFT_OPTION, (120, 0, 120), "top_right"),
(MOD_OPTION_SHIFT, (120, 0, 120), "top_right"),
(MOD_BASE, (0, 0, 0), "bottom_left"),
(MOD_OPTION, (170, 0, 0), "bottom_right"),
]
@@ -202,7 +202,7 @@ class LayoutPDF(FPDF):
((0, 0, 0), "Base"),
((0, 40, 170), "Shift"),
((170, 0, 0), "Option"),
((120, 0, 120), "Shift+Option"),
((120, 0, 120), "Option+Shift"),
]
for color, label in items:
self._color(color)
@@ -316,7 +316,7 @@ class LayoutPDF(FPDF):
"""Find which key combo triggers a dead key state."""
mod_names = {
"0": "", "1": "", "2": "", "3": "",
"4": "", "5": "⇪⌥ ",
"4": " ", "5": "⇪⌥ ",
}
# map key codes to physical labels from KEYBOARD_ROWS
code_labels = {}

View File

@@ -74,9 +74,9 @@ MODIFIER_LABELS = {
1: "Shift",
2: "Caps",
3: "Option",
4: "Shift+Option",
4: "Option+Shift",
5: "Caps+Option",
6: "Command+Option",
6: "Option+Command",
7: "Control",
}

View File

@@ -21,7 +21,7 @@ from parse_keylayout import parse_keylayout, TYPING_KEY_CODES, MODIFIER_LABELS,
BUNDLE_DIR = Path(__file__).parent.parent / "build" / "EurKey-macOS.bundle" / "Contents" / "Resources"
# modifier indices that contain meaningful typing output
# (exclude index 6 = Command+Option and 7 = Control — these are system shortcuts)
# (exclude index 6 = Option+Command and 7 = Control — these are system shortcuts)
VALIDATED_MODIFIER_INDICES = {"0", "1", "2", "3", "4", "5"}
@@ -182,14 +182,14 @@ def format_char_display(c):
# v1.2 predates v1.3 — known differences documented here
V1_2_EXCEPTIONS = {
# Shift+Option S: v1.2 has § where v1.3 has ẞ (capital sharp s)
# Option+Shift S: v1.2 has § where v1.3 has ẞ (capital sharp s)
"4:1": {"output": "§"},
# v1.2 does not have the ¬ (negation) dead key — added in v1.3
# instead, Option+- has the © dead key, and Option+\ outputs plain ¬
"_dead_key_skip": ["dead: ¬"],
"3:27": {"deadKey": "dead: ©"}, # Option+-: © dead key instead of ¬ dead key
"3:42": {"output": "¬"}, # Option+\: plain ¬ instead of ¬ dead key
"4:27": {"output": ""}, # Shift+Option+-: № instead of ✗
"4:27": {"output": ""}, # Option+Shift+-: № instead of ✗
"5:27": {"deadKey": "dead: ©"}, # Caps+Option+-: © dead key instead of ¬ dead key
}