rename v2.0 to EurKEY Next, add ANSI support, normalize Caps Lock, fix v1.4

- rename v2.0 → EurKEY Next across all files, scripts, workflows, website
- rename bundle EurKey-macOS → EurKEY-macOS
- add §/± to ¬ dead key (¬+s → §, ¬+S → ±) for ANSI keyboard compatibility
- normalize Caps Lock = Shift across all four layouts, remove custom Caps bindings
- fix v1.4: revert accidental Caps/ẞ changes, correct changelog to super/subscript swap
- update README: remove duplicate v1.2–v1.4 changelog, reorder versions (Next first)
- update website: ISO+ANSI feature card, EurKEY Next branding, consistent version order

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 20:54:34 +01:00
parent c2274406ec
commit ea4f88ae51
21 changed files with 375 additions and 402 deletions

View File

@@ -10,7 +10,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
BUILD_DIR="${PROJECT_DIR}/build"
BUNDLE_DIR="${BUILD_DIR}/EurKey-macOS.bundle"
BUNDLE_DIR="${BUILD_DIR}/EurKEY-Next.bundle"
CONTENTS_DIR="${BUNDLE_DIR}/Contents"
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
@@ -20,11 +20,11 @@ if [[ "${1:-}" == "--version" ]]; then
VERSION="${2:?missing version argument}"
fi
BUNDLE_ID="de.felixfoertsch.keyboardlayout.EurKEY-macOS"
BUNDLE_NAME="EurKEY-macOS"
BUNDLE_ID="de.felixfoertsch.keyboardlayout.EurKEY-Next"
BUNDLE_NAME="EurKEY-Next"
# layout versions to include
VERSIONS=("v1.2" "v1.3" "v1.4" "v2.0")
# layout names to include (EurKEY Next is the main layout, others are legacy versions)
LAYOUTS=("EurKEY Next" "EurKEY v1.4" "EurKEY v1.3" "EurKEY v1.2")
SRC_DIR="${PROJECT_DIR}/src"
@@ -39,9 +39,9 @@ bash "${SCRIPT_DIR}/build-icons.sh"
rm -rf "${BUNDLE_DIR}"
mkdir -p "${RESOURCES_DIR}"
for ver in "${VERSIONS[@]}"; do
cp "${SRC_DIR}/keylayouts/EurKEY ${ver}.keylayout" "${RESOURCES_DIR}/"
cp "${SRC_DIR}/icons/EurKEY ${ver}.icns" "${RESOURCES_DIR}/"
for layout in "${LAYOUTS[@]}"; do
cp "${SRC_DIR}/keylayouts/${layout}.keylayout" "${RESOURCES_DIR}/"
cp "${SRC_DIR}/icons/${layout}.icns" "${RESOURCES_DIR}/"
done
for lang in en de es; do
@@ -74,15 +74,14 @@ PLIST_VERSION
echo " <string>${VERSION}</string>" >> "${CONTENTS_DIR}/Info.plist"
# add KLInfo for each version
for ver in "${VERSIONS[@]}"; do
layout_name="EurKEY ${ver}"
# generate input source ID: bundle id + layout name with spaces removed, lowercased
source_id_suffix=$(echo "eurkey${ver}" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
# add KLInfo for each layout
for layout in "${LAYOUTS[@]}"; do
# generate input source ID: bundle id + layout name lowercased, spaces removed
source_id_suffix=$(echo "${layout}" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
source_id="${BUNDLE_ID}.${source_id_suffix}"
cat >> "${CONTENTS_DIR}/Info.plist" << KLINFO_ENTRY
<key>KLInfo_${layout_name}</key>
<key>KLInfo_${layout}</key>
<dict>
<key>TICapsLockLanguageSwitchCapable</key>
<true/>
@@ -101,7 +100,7 @@ cat >> "${CONTENTS_DIR}/Info.plist" << 'PLIST_FOOTER'
</plist>
PLIST_FOOTER
echo "Generated Info.plist with ${#VERSIONS[@]} layout entries"
echo "Generated Info.plist with ${#LAYOUTS[@]} layout entries"
# --- generate version.plist ---
cat > "${CONTENTS_DIR}/version.plist" << VPLIST

View File

@@ -12,7 +12,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
BUILD_DIR="${PROJECT_DIR}/build"
BUNDLE_DIR="${BUILD_DIR}/EurKey-macOS.bundle"
BUNDLE_DIR="${BUILD_DIR}/EurKEY-Next.bundle"
# parse arguments
VERSION="${1:-$(date +%Y.%m.%d)}"
@@ -20,7 +20,7 @@ if [[ "${1:-}" == "--version" ]]; then
VERSION="${2:?missing version argument}"
fi
DMG_NAME="EurKEY-macOS-${VERSION}"
DMG_NAME="EurKEY-Next-${VERSION}"
DMG_PATH="${BUILD_DIR}/${DMG_NAME}.dmg"
STAGING_DIR="${BUILD_DIR}/dmg-staging"
@@ -41,7 +41,7 @@ mkdir -p "${STAGING_DIR}"
cp -R "${BUNDLE_DIR}" "${STAGING_DIR}/"
# copy layout PDFs
cp "${BUILD_DIR}"/eurkey-*-layout.pdf "${STAGING_DIR}/"
cp "${BUILD_DIR}"/eurkey-*layout.pdf "${STAGING_DIR}/"
# create symlink to installation target
ln -s "/Library/Keyboard Layouts" "${STAGING_DIR}/Install Here (Keyboard Layouts)"

View File

@@ -4,7 +4,7 @@
# 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).
# The EurKEY Next icon is a monochrome star ring (managed separately).
#
# Usage: bash scripts/build-icons.sh
set -euo pipefail
@@ -54,7 +54,7 @@ rm "${ICONSET}"/tmp_*.png
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)
# install for v1.2, v1.3, v1.4 (EurKEY Next 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"

View File

@@ -443,20 +443,22 @@ def generate_pdf(version, output_dir):
"""Generate a PDF for the given layout version."""
src_dir = Path(__file__).parent.parent / "src" / "keylayouts"
keylayout = src_dir / f"EurKEY {version}.keylayout"
display_name = f"EurKEY {version}"
pdf_name = f"eurkey-{version.lower()}-layout.pdf"
if not keylayout.exists():
print(f"ERROR: {keylayout} not found")
return False
print(f"Generating PDF for EurKEY {version}...")
print(f"Generating PDF for {display_name}...")
data = parse_keylayout(str(keylayout), keyboard_type=0)
pdf = LayoutPDF(f"EurKEY {version}")
pdf = LayoutPDF(display_name)
pdf.generate(data)
out = Path(output_dir)
out.mkdir(parents=True, exist_ok=True)
output_path = out / f"eurkey-{version}-layout.pdf"
output_path = out / pdf_name
pdf.output(str(output_path))
print(f" Written: {output_path}")
return True
@@ -466,7 +468,7 @@ def main():
parser = argparse.ArgumentParser(description="Generate keyboard layout PDFs")
parser.add_argument(
"--version", "-v", nargs="*",
default=["v1.2", "v1.3", "v1.4", "v2.0"],
default=["Next", "v1.4", "v1.3", "v1.2"],
help="Layout versions to generate (default: all)",
)
default_output = str(Path(__file__).parent.parent / "build")

View File

@@ -18,7 +18,7 @@ from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from parse_keylayout import parse_keylayout, TYPING_KEY_CODES, MODIFIER_LABELS, KEY_CODE_NAMES
BUNDLE_DIR = Path(__file__).parent.parent / "build" / "EurKey-macOS.bundle" / "Contents" / "Resources"
BUNDLE_DIR = Path(__file__).parent.parent / "build" / "EurKEY-Next.bundle" / "Contents" / "Resources"
# modifier indices that contain meaningful typing output
# (exclude index 6 = Option+Command and 7 = Control — these are system shortcuts)
@@ -184,27 +184,25 @@ def format_char_display(c):
V1_2_EXCEPTIONS = {
# Option+Shift S: v1.2 has § where v1.3 has ẞ (capital sharp s)
"4:1": {"output": "§"},
"5:1": {"output": "§"}, # Caps+Option S: mirrors Option+Shift (§)
# 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": ""}, # Option+Shift+-: № instead of ✗
"5:27": {"deadKey": "dead: ©"}, # Caps+Option+-: © dead key instead of ¬ dead key
"5:27": {"output": ""}, # Caps+Option+-: mirrors Option+Shift (№)
}
# v1.4 differences from v1.3:
# - §/` key (code 10) in Caps/Caps+Option outputs ẞ instead of §
# - ¬ dead key has an extra ¬ composition (self-referencing)
# - swaps super/subscript numbers in α dead key
# - ¬ dead key has an extra ¬ composition (self-referencing, harmless Ukelele artifact)
V1_4_EXCEPTIONS = {
"2:10": {"output": ""}, # Caps: §/` → ẞ (capital sharp s)
"5:10": {"output": ""}, # Caps+Option: §/` → ẞ
"5:27": {"output": ""}, # Caps+Option+-: no output (missing ¬ dead key in this layer)
"dead:dead: ¬:extra:¬": True, # extra ¬ composition in negation dead key
}
# v2.0 is a custom edition — skip validation for now, just document diffs
V2_0_EXCEPTIONS = {
# EurKEY Next (main edition) — skip validation, custom layout
EURKEY_NEXT_EXCEPTIONS = {
"_skip_validation": True,
}
@@ -212,7 +210,7 @@ VERSIONS = {
"v1.2": {"file": "v1.2", "exceptions": V1_2_EXCEPTIONS, "label": "EurKEY v1.2"},
"v1.3": {"file": "v1.3", "exceptions": {}, "label": "EurKEY v1.3 (reference)"},
"v1.4": {"file": "v1.4", "exceptions": V1_4_EXCEPTIONS, "label": "EurKEY v1.4"},
"v2.0": {"file": "v2.0", "exceptions": V2_0_EXCEPTIONS, "label": "EurKEY v2.0 (custom)"},
"next": {"file": "Next", "exceptions": EURKEY_NEXT_EXCEPTIONS, "label": "EurKEY Next"},
}