keyboard-configurator Svelte Themes

Keyboard Configurator

Web application for customizing Cairn Mesa | Key mechanical keyboards, developed by Cairn Devices. It allows users to visually configure their keyboard: layout selection, graphic themes, fonts, and advanced key remapping.

Mesa | Key Configurator

🇬🇧 English version below

Demo : https://configurator.combe.tf

GitHub repository : https://github.com/ABHC/keyboard-configurator
GitLab repository : https://gitlab.com/ABHC_/keyboard-configurator

Licence / License

  • Hardware : CERN-OHL-S-2.0
  • Software / Logiciel : GPL-3.0-or-later
  • Documentation : CC-BY-SA-4.0

See the LICENSE file for details.


Version Française

Présentation

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 :

  • ISO : disposition europĂ©enne standard
  • Orthogonale (type TypeMatrix) : disposition avec les touches alignĂ©es verticalement

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.

Concept technique principal : la modification du SVG 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 :

Modification du thème/fond

// 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);

Modification de l'opacité du filtre

// Un filtre SVG contrôle l'opacité du fond
const element = dom_keyboard.getElementById("filter");
element.style.opacity = level; // 0.1 Ă  0.8

Modification de la police

// 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);

Modification de l'épaisseur des contours

// 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 : remappage avancé des touches

Le mode expert permet aux utilisateurs de réorganiser complètement leur clavier par glisser-déposer.

Architecture du système

Le mode expert superpose deux couches :

  1. Le squelette SVG (expert_preview_ISO.svg) : une version simplifiée du clavier affichant uniquement les contours des touches
  2. Les touches interactives : des éléments <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}

Système de layers (couches)

Chaque touche peut avoir deux fonctions :

  • Layer 1 : la fonction principale (ex: A, 1, @)
  • Layer 2 : la fonction secondaire accessible via FN (ex: 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
}

Contraintes de déplacement

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;
}

Le Stash (réserve)

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.

Activation des caractères

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

Prévisualisation 3D

L'application propose une vue 3D interactive du clavier assemblé, permettant à l'utilisateur de visualiser le rendu final de sa configuration.

Architecture technique

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)
];

Texture dynamique des touches

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 }

ContrĂ´les interactifs

L'utilisateur peut manipuler la vue 3D grâce aux OrbitControls de Three.js :

  • Rotation : clic gauche + glisser
  • Zoom : molette de la souris
  • Pan : clic droit + glisser

Gestion des ressources

Le composant gère proprement le cycle de vie des ressources WebGL :

  • Chargement asynchrone des modèles STL avec AbortController pour annulation
  • Nettoyage complet Ă  la destruction (geometries, materials, textures, renderer)
  • GĂ©nĂ©ration automatique des coordonnĂ©es UV pour le mapping de texture

Stack technique

Frontend :

  • SvelteKit 2 + Svelte 4
  • TypeScript
  • Three.js (visualisation 3D)
  • Vite

Assets :

  • Fichiers SVG pour les layouts
  • Fichiers STL pour les modèles 3D
  • Fichiers JSON pour les configurations (layouts, thèmes, polices, etc.)

Lancer le projet

cd frontend
npm install
npm run dev

L'application sera accessible sur http://localhost:5173


Structure des fichiers principaux

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

English Version

Overview

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:

  • ISO: standard European layout
  • Orthogonal (TypeMatrix-style): layout with vertically aligned keys

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.

Core Technical Concept: Real-time SVG Modification

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:

Theme/Background Modification

// The background is an <image> element in the SVG with id "backgroundImage"
const imageElement = dom_keyboard.getElementById("backgroundImage");
imageElement.setAttribute("xlink:href", base64_image);

Filter Opacity Modification

// An SVG filter controls the background opacity
const element = dom_keyboard.getElementById("filter");
element.style.opacity = level; // 0.1 to 0.8

Font Modification

// 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);

Stroke Width Modification

// 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: Advanced Key Remapping

Expert mode allows users to completely reorganize their keyboard through drag-and-drop.

System Architecture

Expert mode overlays two layers:

  1. The SVG skeleton (expert_preview_ISO.svg): a simplified keyboard version showing only key outlines
  2. Interactive keys: <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}

Layer System

Each key can have two functions:

  • Layer 1: the primary function (e.g., A, 1, @)
  • Layer 2: the secondary function accessible via FN (e.g., 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
}

Movement Constraints

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;
}

The Stash (Reserve)

Keys removed from the keyboard are stored in a "stash" (KeyStash). Users can drag them from this stash to an empty keyboard slot.

Character Activation

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

3D Preview

The application offers an interactive 3D view of the assembled keyboard, allowing users to visualize the final rendering of their configuration.

Technical Architecture

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)
];

Dynamic Keycap Texture

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 }

Interactive Controls

Users can manipulate the 3D view using Three.js OrbitControls:

  • Rotate: left click + drag
  • Zoom: mouse wheel
  • Pan: right click + drag

Resource Management

The component properly manages WebGL resource lifecycle:

  • Asynchronous STL model loading with AbortController for cancellation
  • Complete cleanup on destruction (geometries, materials, textures, renderer)
  • Automatic UV coordinate generation for texture mapping

Tech Stack

Frontend:

  • SvelteKit 2 + Svelte 4
  • TypeScript
  • Three.js (3D visualization)
  • Vite

Assets:

  • SVG files for layouts
  • STL files for 3D models
  • JSON files for configurations (layouts, themes, fonts, etc.)

Running the Project

cd frontend
npm install
npm run dev

The application will be available at http://localhost:5173


Main File Structure

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

Top categories

Loading Svelte Themes