mirror of
https://github.com/felixfoertsch/EurKEY-macOS.git
synced 2026-04-16 06:28:28 +02:00
update v2.0 layout, fix keyboard viewer dead key cycling, clean up repo
- remove duplicate compositions from v2.0: § from Navigators, ± from Option+Shift §, ± from Mathematicians on -, terminator dupes on 5 - change Mathematicians terminator from space to 𝕄 - fix keyboard viewer: support multiple dead keys per key with click cycling, show active dead key's own layer character, hide others - add feature card SVG icons (dead keys, ISO enter, versions, install) - delete unused draft SVGs, remove leftover docs/ PDFs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- Draft C: Single key cap with EU star — minimal, reads well at any size -->
|
||||
<rect width="1024" height="1024" rx="180" fill="#003399"/>
|
||||
<!-- Large key cap shape, centered -->
|
||||
<rect x="172" y="172" width="680" height="680" rx="80" fill="rgba(255,255,255,0.92)"
|
||||
stroke="rgba(255,255,255,0.3)" stroke-width="8"/>
|
||||
<!-- Key shadow/depth -->
|
||||
<rect x="172" y="180" width="680" height="680" rx="80" fill="none"
|
||||
stroke="rgba(0,0,0,0.08)" stroke-width="8"/>
|
||||
<!-- Single EU gold star centered on key -->
|
||||
<polygon points="512,220 568,398 756,398 604,506 646,680 512,580 378,680 420,506 268,398 456,398"
|
||||
fill="#FFCC00"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 699 B |
@@ -1,7 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- Template icon: "EU" — transparent bg, black text
|
||||
macOS TISIconIsTemplate renders this adaptively:
|
||||
light mode: dark text, light pill background
|
||||
dark mode: light text, dark pill background -->
|
||||
<text x="512" y="700" text-anchor="middle" font-family="'SF Pro Text', '.AppleSystemUIFont', 'Helvetica Neue', sans-serif" font-size="620" font-weight="600" fill="#000" letter-spacing="-10">EU</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 490 B |
@@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- Template icon: "EUR" text — macOS renders black in light mode, white in dark mode -->
|
||||
<text x="512" y="640" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', sans-serif" font-size="480" font-weight="700" fill="#000" letter-spacing="-20">EUR</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 384 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
8
eurkey-macos.eu/img/icon-deadkeys.svg
Normal file
8
eurkey-macos.eu/img/icon-deadkeys.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<!-- Key cap with accent mark -->
|
||||
<rect x="8" y="12" width="32" height="28" rx="4" stroke="#003399" stroke-width="2.5" fill="none"/>
|
||||
<!-- Letter a on key -->
|
||||
<text x="24" y="35" text-anchor="middle" font-family="-apple-system, system-ui, sans-serif" font-size="16" font-weight="700" fill="#003399">a</text>
|
||||
<!-- Accent mark floating above key -->
|
||||
<path d="M20 8 L24 4 L28 8" stroke="#003399" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 558 B |
7
eurkey-macos.eu/img/icon-iso.svg
Normal file
7
eurkey-macos.eu/img/icon-iso.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<!-- ISO Enter key shape (inverted L) -->
|
||||
<path d="M18 6 H38 Q42 6 42 10 V38 Q42 42 38 42 H26 Q22 42 22 38 V22 H10 Q6 22 6 18 V10 Q6 6 10 6 Z" stroke="#003399" stroke-width="2.5" fill="none"/>
|
||||
<!-- Enter arrow -->
|
||||
<path d="M36 18 V30 H28" stroke="#003399" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M31 27 L28 30 L31 33" stroke="#003399" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 521 B |
@@ -54,22 +54,22 @@
|
||||
<h2>Features</h2>
|
||||
<div class="feature-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⌥</div>
|
||||
<div class="feature-icon"><img src="img/icon-deadkeys.svg" alt="" width="36" height="36"></div>
|
||||
<h3>Dead Keys for Diacritics</h3>
|
||||
<p>Type accented characters (ä, é, ñ, č, …) using intuitive Option-key combinations. No character palette needed.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⌨</div>
|
||||
<div class="feature-icon"><img src="img/icon-iso.svg" alt="" width="36" height="36"></div>
|
||||
<h3>ISO International Layout</h3>
|
||||
<p>Built for the physical English International keyboard found on European MacBooks — with the extra § key and big Enter.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">◇</div>
|
||||
<div class="feature-icon"><img src="img/icon-versions.svg" alt="" width="36" height="36"></div>
|
||||
<h3>Multiple Versions</h3>
|
||||
<p>Ships v1.2, v1.3, v1.4, and v2.0 in a single bundle. Pick the one that fits your workflow.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">↓</div>
|
||||
<div class="feature-icon"><img src="img/icon-install.svg" alt="" width="36" height="36"></div>
|
||||
<h3>Easy Install</h3>
|
||||
<p>Download the DMG, drag the bundle to Keyboard Layouts, log out. Done in under a minute.</p>
|
||||
</div>
|
||||
|
||||
@@ -63,7 +63,7 @@ const DEAD_KEY_NAMES = {
|
||||
"\u00af": "The Macrons", "\u02da": "The Rings & Dots",
|
||||
"\u03b1": "The Greeks", "\u221a": "The Mathematicians",
|
||||
"\u00ac": "The Navigators", "\u00a9": "The Navigators",
|
||||
" ": "The Mathematicians",
|
||||
"\uD835\uDD44": "The Mathematicians",
|
||||
};
|
||||
|
||||
const cache = new Map();
|
||||
@@ -186,8 +186,7 @@ function renderKeyboard(data) {
|
||||
} else {
|
||||
keyElements.set(keyCode, keyEl);
|
||||
|
||||
let hasDead = false;
|
||||
let deadState = null;
|
||||
const deadStates = [];
|
||||
|
||||
for (const layer of LAYERS) {
|
||||
const info = charForKey(data, layer.mod, keyCode);
|
||||
@@ -196,18 +195,19 @@ function renderKeyboard(data) {
|
||||
if (info) {
|
||||
span.textContent = displayChar(info.char);
|
||||
if (info.deadKey) {
|
||||
hasDead = true;
|
||||
deadState = info.deadKey;
|
||||
if (!deadStates.includes(info.deadKey)) deadStates.push(info.deadKey);
|
||||
span.classList.add("key-char--is-dead");
|
||||
}
|
||||
}
|
||||
keyEl.appendChild(span);
|
||||
}
|
||||
|
||||
if (hasDead) {
|
||||
if (deadStates.length > 0) {
|
||||
deadStates.reverse();
|
||||
keyEl.classList.add("key--dead");
|
||||
keyEl.dataset.deadKey = deadState;
|
||||
keyEl.addEventListener("click", () => toggleDeadKeyMode(deadState));
|
||||
keyEl.dataset.deadKey = deadStates[0];
|
||||
keyEl.dataset.deadKeys = JSON.stringify(deadStates);
|
||||
keyEl.addEventListener("click", () => cycleDeadKeyMode(keyEl));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,23 @@ function toggleDeadKeyMode(deadState) {
|
||||
}
|
||||
}
|
||||
|
||||
function cycleDeadKeyMode(keyEl) {
|
||||
const deadStates = JSON.parse(keyEl.dataset.deadKeys || "[]");
|
||||
if (deadStates.length === 0) return;
|
||||
|
||||
if (!currentDeadKey || !deadStates.includes(currentDeadKey)) {
|
||||
enterDeadKeyMode(deadStates[0]);
|
||||
} else {
|
||||
const idx = deadStates.indexOf(currentDeadKey);
|
||||
const next = idx + 1;
|
||||
if (next < deadStates.length) {
|
||||
enterDeadKeyMode(deadStates[next]);
|
||||
} else {
|
||||
exitDeadKeyMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enterDeadKeyMode(deadState) {
|
||||
if (!currentData) return;
|
||||
const charMap = buildCompositionMap(currentData, deadState);
|
||||
@@ -260,21 +277,35 @@ function enterDeadKeyMode(deadState) {
|
||||
const baseComposed = charMap[baseChar] || "";
|
||||
const shiftComposed = charMap[shiftChar] || "";
|
||||
|
||||
const spans = keyEl.querySelectorAll(".key-char");
|
||||
// order: shift, option-shift, base, option
|
||||
if (spans[0]) spans[0].textContent = displayChar(shiftComposed);
|
||||
if (spans[1]) spans[1].textContent = "";
|
||||
if (spans[2]) spans[2].textContent = displayChar(baseComposed);
|
||||
if (spans[3]) spans[3].textContent = "";
|
||||
|
||||
if (keyEl.dataset.deadKey === deadState) {
|
||||
const allDead = JSON.parse(keyEl.dataset.deadKeys || "[]");
|
||||
if (allDead.includes(deadState)) {
|
||||
keyEl.classList.add("key--dead-active");
|
||||
} else if (baseComposed || shiftComposed) {
|
||||
keyEl.classList.add("key--has-composition");
|
||||
keyEl.classList.remove("key--no-composition");
|
||||
// only show the span for the layer that owns this dead key
|
||||
const spans = keyEl.querySelectorAll(".key-char");
|
||||
const layerOrder = [MOD_SHIFT, MOD_OPTION_SHIFT, MOD_BASE, MOD_OPTION];
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const info = charForKey(currentData, layerOrder[i], keyCode);
|
||||
if (info?.deadKey === deadState) {
|
||||
spans[i].style.visibility = "visible";
|
||||
} else {
|
||||
spans[i].style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keyEl.classList.add("key--no-composition");
|
||||
keyEl.classList.remove("key--has-composition");
|
||||
const spans = keyEl.querySelectorAll(".key-char");
|
||||
// order: shift, option-shift, base, option
|
||||
if (spans[0]) spans[0].textContent = displayChar(shiftComposed);
|
||||
if (spans[1]) spans[1].textContent = "";
|
||||
if (spans[2]) spans[2].textContent = displayChar(baseComposed);
|
||||
if (spans[3]) spans[3].textContent = "";
|
||||
|
||||
if (baseComposed || shiftComposed) {
|
||||
keyEl.classList.add("key--has-composition");
|
||||
keyEl.classList.remove("key--no-composition");
|
||||
} else {
|
||||
keyEl.classList.add("key--no-composition");
|
||||
keyEl.classList.remove("key--has-composition");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,6 +333,7 @@ function exitDeadKeyMode() {
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const info = charForKey(currentData, layerOrder[i], keyCode);
|
||||
spans[i].textContent = info ? displayChar(info.char) : "";
|
||||
spans[i].style.visibility = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@
|
||||
<key code="7" output="Á"/>
|
||||
<key code="8" output="Ç"/>
|
||||
<key code="9" output="Ì"/>
|
||||
<key code="10" output="±"/>
|
||||
<key code="10" output=""/>
|
||||
<key code="11" output="Í"/>
|
||||
<key code="12" action="Æ"/>
|
||||
<key code="13" output="Å"/>
|
||||
@@ -1093,7 +1093,6 @@
|
||||
<action id="-">
|
||||
<when state="none" output="-"/>
|
||||
<when state="⌥m" output="⁻"/>
|
||||
<when state="⌥⇧m" output="±"/>
|
||||
</action>
|
||||
<action id=".">
|
||||
<when state="none" output="."/>
|
||||
@@ -1132,10 +1131,6 @@
|
||||
<action id="5">
|
||||
<when state="none" output="5"/>
|
||||
<when state="⌥\" output="⅔"/>
|
||||
<when state="⌥⇧7" output="¯"/>
|
||||
<when state="⌥'" output="´"/>
|
||||
<when state="⌥⇧6" output="ˇ"/>
|
||||
<when state="⌥7" output="˚"/>
|
||||
<when state="⌥m" output="₅"/>
|
||||
</action>
|
||||
<action id="6">
|
||||
@@ -1584,7 +1579,6 @@
|
||||
<action id="s">
|
||||
<when state="none" output="s"/>
|
||||
<when state="⌥6" output="ŝ"/>
|
||||
<when state="⌥\" output="§"/>
|
||||
<when state="⌥'" output="ś"/>
|
||||
<when state="⌥⇧6" output="š"/>
|
||||
<when state="⌥m" output="σ"/>
|
||||
@@ -1775,6 +1769,6 @@
|
||||
<when state="⌥⇧6" output="ˇ"/>
|
||||
<when state="⌥7" output="˚"/>
|
||||
<when state="⌥m" output="α"/>
|
||||
<when state="⌥⇧m" output=" "/>
|
||||
<when state="⌥⇧m" output="𝕄"/>
|
||||
</terminators>
|
||||
</keyboard>
|
||||
|
||||
Reference in New Issue
Block a user