🇬🇧 English version below
Demo : https://configurator.combe.tf
GitHub repository : https://github.com/ABHC/keyboard-configurator
GitLab repository : https://gitlab.com/ABHC_/keyboard-configurator
See the LICENSE file for details.
Mesa Key Configurator est une application web de personnalisation de clavier mécanique développée par Cairn Devices. Elle permet aux utilisateurs de configurer visuellement leur clavier : choix du layout, du thème graphique, des polices, et remappage avancé des touches.
Important : Ce configurateur est spécifiquement conçu pour les claviers Cairn Mesa | Key et ne prend en charge que deux architectures :
Note : Ce projet est archivé. Il est maintenu en ligne comme démo technique et peut servir d'inspiration pour des problématiques similaires de personnalisation de produit en temps réel.
Le cœur de l'application repose sur un principe simple mais puissant : un fichier SVG de base est modifié dynamiquement selon les options choisies par l'utilisateur.
Plutôt que de pré-générer des dizaines de combinaisons d'images, l'application charge un SVG template (ISO-Base.svg ou ORTHO-Base.svg) qui contient tous les éléments du clavier. Chaque personnalisation de l'utilisateur modifie directement le DOM de ce SVG en mémoire :
// Le fond est un élément <image> dans le SVG avec l'id "backgroundImage"
const imageElement = dom_keyboard.getElementById("backgroundImage");
imageElement.setAttribute("xlink:href", base64_image);
// Un filtre SVG contrôle l'opacité du fond
const element = dom_keyboard.getElementById("filter");
element.style.opacity = level; // 0.1 Ă 0.8
// La police est embarquée via une balise <style> dans le SVG
const font_face = `@font-face { font-family: '${font_name}'; src: url('${font_url}') format('truetype'); }`;
const style_tag = `<style id="embedded-font">${font_face}</style>`;
svgElement.innerHTML = `${style_tag}${svgElement.innerHTML}`;
// Puis remplacement global du nom de police dans le SVG
svgElement.innerHTML = svgElement.innerHTML.replace(new RegExp(old_font, 'gi'), new_font);
// Chaque touche a des éléments "shadow" pour le contour du texte
const shadowElements = document.querySelectorAll('[id*="-shadow"]');
shadowElements.forEach(element => {
element.style.strokeWidth = stroke + 'px';
});
Cette approche permet une prévisualisation instantanée sans aller-retour serveur, et le SVG final peut être exporté tel quel pour la production.
Le mode expert permet aux utilisateurs de réorganiser complètement leur clavier par glisser-déposer.
Le mode expert superpose deux couches :
expert_preview_ISO.svg) : une version simplifiée du clavier affichant uniquement les contours des touches<div> positionnés en absolu au-dessus du SVG, chacun représentant une touche draggable<object class="skeletton" data="architectures/expert_preview_ISO.svg" type="image/svg+xml" />
{#each Object.entries($keymap) as [key_id, key]}
<div
class="key"
style="left: {key.x}px; top: {key.y}px; width: {key.width}px; height: {key.height}px;"
draggable={isDraggable(key)}
on:dragstart={() => handleDragStart(key_id)}
on:drop={(event) => handleDrop(event, key_id)}
>
<!-- Contenu de la touche -->
</div>
{/each}
Chaque touche peut avoir deux fonctions :
A, 1, @)F1, Play, Vol+)L'utilisateur peut basculer entre l'édition du Layer 1 et du Layer 2, ou afficher les deux simultanément :
// Structure d'une touche
{
field1: "A", // Caractère principal
field2: "a", // Caractère secondaire (minuscule)
field3: "", // Caractère tertiaire
keycode_field1: "KC_A", // Keycode QMK
layer2: "F1", // Fonction Layer 2
keycode_layer2: "KC_F1",
fixed: false, // Touche fixe (non déplaçable)
hasLayerLed: true, // Indicateur LED pour le layer 2
opacity: true // Visibilité de la touche
}
Certaines touches sont protégées :
function isDraggable(key) {
if (key.fixed === true) return false; // Touches fixes (Espace, etc.)
if ($layer2_editor && !key.layer2) return false; // Pas de layer2 sur touches aveugles
if (key.opacity === false) return false; // Emplacements vides
return true;
}
Les touches retirées du clavier sont stockées dans une "réserve" (KeyStash). L'utilisateur peut les glisser depuis cette réserve vers un emplacement vide du clavier.
Le centre d'aide de l'application explique comment activer les différents caractères selon leur position sur la touche :
| Position sur la touche | Comment l'activer |
|---|---|
| Caractère principal (field1) | Appui direct sur la touche |
| Caractère secondaire (field2) | Shift + touche |
| Caractère tertiaire (field3) | AltGr + touche |
| Fonction Layer 2 | FN + touche |
L'application propose une vue 3D interactive du clavier assemblé, permettant à l'utilisateur de visualiser le rendu final de sa configuration.
La prévisualisation utilise Three.js pour le rendu WebGL. Le clavier est composé de plusieurs modèles STL assemblés :
const MODELS_CONFIG = [
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }, // Touches
{ file: '/models/Tole-superieure-ISO.stl', texture: 'texture_alu.jpg' }, // Plaque alu
{ file: '/models/Casing.stl', color: casing_color }, // Boîtier
{ file: '/models/Amortisseur-ISO.stl', color: '#000000' }, // Amortisseur
{ file: '/models/Plaque-magnetique.stl', texture: '...' }, // Plaque magnétique
{ file: '/models/Patins.stl', color: '#444444' }, // Patins
{ file: '/models/Galets.stl', color: '#444444' }, // Galets (optionnel)
];
Le SVG personnalisé par l'utilisateur est converti en texture et appliqué sur le modèle 3D des keycaps :
// La texture des touches est générée à partir du SVG configuré
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }
L'utilisateur peut manipuler la vue 3D grâce aux OrbitControls de Three.js :
Le composant gère proprement le cycle de vie des ressources WebGL :
AbortController pour annulationFrontend :
Assets :
cd frontend
npm install
npm run dev
L'application sera accessible sur http://localhost:5173
frontend/
├── src/routes/
│ ├── general/ # Mode standard
│ │ ├── +page.svelte # Page principale
│ │ ├── DesignPanel.svelte # Thèmes, opacité, couleurs
│ │ ├── BasePanel.svelte # Polices, switches
│ │ └── LayoutPanel.svelte # Choix du layout
│ ├── expert/ # Mode expert
│ │ ├── +page.svelte # Éditeur de keymap
│ │ ├── SkelettonKeyboard.svelte # Clavier interactif
│ │ └── KeyStash.svelte # Réserve de touches
│ ├── store.js # État global (Svelte stores)
│ └── svgEditor.js # Utilitaires de modification SVG
├── static/
│ ├── architectures/ # SVG de base (ISO, ORTHO)
│ ├── layouts_config/ # Configurations de layouts
│ ├── json/ # Données (thèmes, polices, etc.)
│ └── models/ # Fichiers STL pour la 3D
Mesa Key Configurator is a web application for customizing mechanical keyboards, developed by Cairn Devices. It allows users to visually configure their keyboard: layout selection, graphic themes, fonts, and advanced key remapping.
Important: This configurator is specifically designed for Cairn Mesa | Key keyboards and only supports two architectures:
Note: This project is archived. It is kept online as a technical demo and may serve as inspiration for similar real-time product customization challenges.
The heart of the application relies on a simple but powerful principle: a base SVG file is dynamically modified according to user-selected options.
Rather than pre-generating dozens of image combinations, the application loads an SVG template (ISO-Base.svg or ORTHO-Base.svg) containing all keyboard elements. Each user customization directly modifies this SVG's DOM in memory:
// The background is an <image> element in the SVG with id "backgroundImage"
const imageElement = dom_keyboard.getElementById("backgroundImage");
imageElement.setAttribute("xlink:href", base64_image);
// An SVG filter controls the background opacity
const element = dom_keyboard.getElementById("filter");
element.style.opacity = level; // 0.1 to 0.8
// The font is embedded via a <style> tag in the SVG
const font_face = `@font-face { font-family: '${font_name}'; src: url('${font_url}') format('truetype'); }`;
const style_tag = `<style id="embedded-font">${font_face}</style>`;
svgElement.innerHTML = `${style_tag}${svgElement.innerHTML}`;
// Then global replacement of the font name in the SVG
svgElement.innerHTML = svgElement.innerHTML.replace(new RegExp(old_font, 'gi'), new_font);
// Each key has "shadow" elements for text outline
const shadowElements = document.querySelectorAll('[id*="-shadow"]');
shadowElements.forEach(element => {
element.style.strokeWidth = stroke + 'px';
});
This approach enables instant preview without server round-trips, and the final SVG can be exported as-is for production.
Expert mode allows users to completely reorganize their keyboard through drag-and-drop.
Expert mode overlays two layers:
expert_preview_ISO.svg): a simplified keyboard version showing only key outlines<div> elements absolutely positioned above the SVG, each representing a draggable key<object class="skeletton" data="architectures/expert_preview_ISO.svg" type="image/svg+xml" />
{#each Object.entries($keymap) as [key_id, key]}
<div
class="key"
style="left: {key.x}px; top: {key.y}px; width: {key.width}px; height: {key.height}px;"
draggable={isDraggable(key)}
on:dragstart={() => handleDragStart(key_id)}
on:drop={(event) => handleDrop(event, key_id)}
>
<!-- Key content -->
</div>
{/each}
Each key can have two functions:
A, 1, @)F1, Play, Vol+)Users can switch between editing Layer 1 and Layer 2, or display both simultaneously:
// Key structure
{
field1: "A", // Main character
field2: "a", // Secondary character (lowercase)
field3: "", // Tertiary character
keycode_field1: "KC_A", // QMK keycode
layer2: "F1", // Layer 2 function
keycode_layer2: "KC_F1",
fixed: false, // Fixed key (non-movable)
hasLayerLed: true, // LED indicator for layer 2
opacity: true // Key visibility
}
Certain keys are protected:
function isDraggable(key) {
if (key.fixed === true) return false; // Fixed keys (Space, etc.)
if ($layer2_editor && !key.layer2) return false; // No layer2 on blind keys
if (key.opacity === false) return false; // Empty slots
return true;
}
Keys removed from the keyboard are stored in a "stash" (KeyStash). Users can drag them from this stash to an empty keyboard slot.
The application's help center explains how to activate different characters based on their position on the key:
| Position on key | How to activate |
|---|---|
| Main character (field1) | Direct key press |
| Secondary character (field2) | Shift + key |
| Tertiary character (field3) | AltGr + key |
| Layer 2 function | FN + key |
The application offers an interactive 3D view of the assembled keyboard, allowing users to visualize the final rendering of their configuration.
The preview uses Three.js for WebGL rendering. The keyboard is composed of several assembled STL models:
const MODELS_CONFIG = [
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }, // Keys
{ file: '/models/Tole-superieure-ISO.stl', texture: 'texture_alu.jpg' }, // Alu plate
{ file: '/models/Casing.stl', color: casing_color }, // Case
{ file: '/models/Amortisseur-ISO.stl', color: '#000000' }, // Dampener
{ file: '/models/Plaque-magnetique.stl', texture: '...' }, // Magnetic plate
{ file: '/models/Patins.stl', color: '#444444' }, // Feet
{ file: '/models/Galets.stl', color: '#444444' }, // Pebbles (optional)
];
The user-customized SVG is converted to a texture and applied to the 3D keycaps model:
// The keycap texture is generated from the configured SVG
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }
Users can manipulate the 3D view using Three.js OrbitControls:
The component properly manages WebGL resource lifecycle:
AbortController for cancellationFrontend:
Assets:
cd frontend
npm install
npm run dev
The application will be available at http://localhost:5173
frontend/
├── src/routes/
│ ├── general/ # Standard mode
│ │ ├── +page.svelte # Main page
│ │ ├── DesignPanel.svelte # Themes, opacity, colors
│ │ ├── BasePanel.svelte # Fonts, switches
│ │ └── LayoutPanel.svelte # Layout selection
│ ├── expert/ # Expert mode
│ │ ├── +page.svelte # Keymap editor
│ │ ├── SkelettonKeyboard.svelte # Interactive keyboard
│ │ └── KeyStash.svelte # Key stash
│ ├── store.js # Global state (Svelte stores)
│ └── svgEditor.js # SVG modification utilities
├── static/
│ ├── architectures/ # Base SVGs (ISO, ORTHO)
│ ├── layouts_config/ # Layout configurations
│ ├── json/ # Data (themes, fonts, etc.)
│ └── models/ # STL files for 3D