keychron-keyboards-hardware-design

Work with Keychron's source-available CAD hardware design files (STEP/DXF/PDF) for keyboards and mice, including scripting, inventory management, and 3D printing workflows.

22 stars

Best use case

keychron-keyboards-hardware-design is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Work with Keychron's source-available CAD hardware design files (STEP/DXF/PDF) for keyboards and mice, including scripting, inventory management, and 3D printing workflows.

Teams using keychron-keyboards-hardware-design should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/keychron-keyboards-hardware-design/SKILL.md --create-dirs "https://raw.githubusercontent.com/Aradotso/trending-skills/main/skills/keychron-keyboards-hardware-design/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/keychron-keyboards-hardware-design/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How keychron-keyboards-hardware-design Compares

Feature / Agentkeychron-keyboards-hardware-designStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Work with Keychron's source-available CAD hardware design files (STEP/DXF/PDF) for keyboards and mice, including scripting, inventory management, and 3D printing workflows.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Keychron Keyboards Hardware Design

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

## What This Project Is

Keychron's `Keychron-Keyboards-Hardware-Design` repository provides production-grade industrial design files for Keychron keyboards and mice. These are real CAD files — not approximations — covering cases, plates, stabilizers, encoders, keycaps, and full assembly models.

**Formats included:**
- `.stp` / `.step` — STEP (ISO 10303) 3D solid models, importable in FreeCAD, Fusion 360, SolidWorks, CATIA, etc.
- `.dxf` — 2D Drawing Exchange Format, used for plates and cutouts in laser cutting or CNC workflows
- `.dwg` — AutoCAD native drawing format
- `.pdf` — Dimensioned engineering drawings for reference

**Series covered:**
| Series | Notable Models |
|---|---|
| Q Series | Q1–Q12, Q0 Plus, Q60, Q65 |
| Q Pro Series | Q1 Pro–Q14 Pro |
| Q HE Series | Q1 HE, Q3 HE, Q5 HE, Q6 HE |
| K Pro Series | K1 Pro–K17 Pro |
| K Max Series | K1 Max–K17 Max |
| K HE Series | K2 HE–K10 HE |
| L Series | L1, L3 |
| V Max Series | V1 Max–V10 Max |
| P HE Series | P1 HE |
| Mice | M1–M7, G1, G2 |

**License:** Source-available. Personal, educational, and non-commercial use only. Commercial use is strictly prohibited.

---

## Getting the Files

### Clone the full repository

```bash
git clone https://github.com/Keychron/Keychron-Keyboards-Hardware-Design.git
cd Keychron-Keyboards-Hardware-Design
```

### Sparse checkout (single model, saves bandwidth)

```bash
git clone --filter=blob:none --sparse https://github.com/Keychron/Keychron-Keyboards-Hardware-Design.git
cd Keychron-Keyboards-Hardware-Design
git sparse-checkout set "Q-Series/Q1"
```

### Sparse checkout for a full series

```bash
git sparse-checkout set "K-Pro-Series"
```

---

## Directory Layout

```
Q-Series/
  Q1/
    Q1-Case.stp
    Q1-Plate.stp
    Q1-Encoder.stp
    Q1-Stabilizer.stp
    Q1-Full-Model.stp
    Q1-OSA-Keycap.stp
Q-Pro-Series/
  Q1 Pro/
    Q1-Pro-Case.stp
    Q1-Pro-Plate.dxf
    ...
K-Pro-Series/
  K6 Pro/
  K8 Pro/
    K8-Pro-Keycap.stp
V-Max-Series/
  V1 Max/
K-Max-Series/
  K8 Max/
    K8-Max-Keycap.stp
K-HE-Series/
  K2 HE/
    K2-HE-Cherry-Keycap.stp
    K2-HE-OSA-Keycap.stp
Mice/
  M1/
    M1-Shell.stp
    M1-Full-Model.stp
Keycap Profiles/
  OSA Profile/
  KSA Profile/
docs/
  file-format-guide.md
  getting-started.md
  3d-printing-guide.md
  repo-inventory.md
  license-faq.md
```

---

## Python Scripts for Working With the Repository

### Inventory all design files

```python
"""
inventory.py — Scan the repo and produce a structured inventory of all design files.
"""
import os
import json
from pathlib import Path
from collections import defaultdict

REPO_ROOT = Path(__file__).parent  # adjust if running from elsewhere
SUPPORTED_EXTENSIONS = {".stp", ".step", ".dxf", ".dwg", ".pdf"}

def build_inventory(root: Path) -> dict:
    inventory = defaultdict(lambda: defaultdict(list))
    for path in sorted(root.rglob("*")):
        if path.suffix.lower() in SUPPORTED_EXTENSIONS:
            # Series = top-level folder; model = second-level folder
            parts = path.relative_to(root).parts
            series = parts[0] if len(parts) > 0 else "Unknown"
            model = parts[1] if len(parts) > 1 else "Root"
            inventory[series][model].append({
                "file": path.name,
                "format": path.suffix.lower().lstrip(".").upper(),
                "size_kb": round(path.stat().st_size / 1024, 1),
                "path": str(path.relative_to(root)),
            })
    return inventory

def print_summary(inventory: dict):
    total_files = 0
    for series, models in inventory.items():
        series_count = sum(len(files) for files in models.values())
        total_files += series_count
        print(f"\n{series} ({len(models)} models, {series_count} files)")
        for model, files in models.items():
            formats = sorted({f["format"] for f in files})
            print(f"  {model}: {len(files)} files [{', '.join(formats)}]")
    print(f"\nTotal: {total_files} design files across {len(inventory)} series")

if __name__ == "__main__":
    inv = build_inventory(REPO_ROOT)
    print_summary(inv)
    # Optional: dump to JSON
    with open("inventory.json", "w") as f:
        json.dump(inv, f, indent=2)
    print("\nInventory saved to inventory.json")
```

Run it:

```bash
python inventory.py
```

---

### Find all plate files (DXF) for laser cutting

```python
"""
find_plates.py — List all DXF plate files suitable for laser cutting or CNC.
"""
from pathlib import Path

REPO_ROOT = Path(".")

def find_plates(root: Path):
    results = []
    for path in sorted(root.rglob("*.dxf")):
        name_lower = path.name.lower()
        if "plate" in name_lower:
            results.append(path)
    return results

if __name__ == "__main__":
    plates = find_plates(REPO_ROOT)
    print(f"Found {len(plates)} plate DXF files:\n")
    for p in plates:
        print(f"  {p}")
```

---

### Search for a specific model's files

```python
"""
find_model.py — Find all files for a given keyboard model.

Usage:
    python find_model.py "Q8"
    python find_model.py "K8 Pro"
    python find_model.py "M3"
"""
import sys
from pathlib import Path

REPO_ROOT = Path(".")

def find_model_files(root: Path, query: str):
    query_lower = query.lower().replace(" ", "")
    matches = []
    for path in sorted(root.rglob("*")):
        if path.is_file():
            # Check folder name or filename
            normalized = str(path).lower().replace(" ", "").replace("-", "")
            if query_lower.replace("-", "") in normalized:
                matches.append(path)
    return matches

if __name__ == "__main__":
    query = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "Q8"
    results = find_model_files(REPO_ROOT, query)
    if not results:
        print(f"No files found matching '{query}'")
    else:
        print(f"Files matching '{query}':\n")
        for r in results:
            print(f"  {r}")
```

---

### Export an inventory CSV for spreadsheet use

```python
"""
export_csv.py — Export a CSV of all design files with metadata.
"""
import csv
from pathlib import Path

REPO_ROOT = Path(".")
OUTPUT = Path("keychron_inventory.csv")
SUPPORTED_EXTENSIONS = {".stp", ".step", ".dxf", ".dwg", ".pdf"}

def export_csv(root: Path, output: Path):
    rows = []
    for path in sorted(root.rglob("*")):
        if path.suffix.lower() in SUPPORTED_EXTENSIONS:
            parts = path.relative_to(root).parts
            series = parts[0] if len(parts) > 0 else ""
            model = parts[1] if len(parts) > 1 else ""
            component = _infer_component(path.name)
            rows.append({
                "series": series,
                "model": model,
                "component": component,
                "filename": path.name,
                "format": path.suffix.lower().lstrip(".").upper(),
                "size_kb": round(path.stat().st_size / 1024, 1),
                "relative_path": str(path.relative_to(root)),
            })

    with open(output, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=rows[0].keys())
        writer.writeheader()
        writer.writerows(rows)
    print(f"Exported {len(rows)} rows to {output}")

def _infer_component(filename: str) -> str:
    name = filename.lower()
    for keyword in ["case", "plate", "encoder", "stabilizer", "keycap",
                    "full-model", "full_model", "shell", "knob"]:
        if keyword.replace("-", "") in name.replace("-", "").replace("_", ""):
            return keyword.replace("-", " ").title()
    return "Other"

if __name__ == "__main__":
    export_csv(REPO_ROOT, OUTPUT)
```

---

### Validate repo file naming conventions

```python
"""
validate_naming.py — Check that files follow Keychron naming conventions.
Expected pattern: <ModelName>-<Component>.<ext>
Example: Q8-Plate.stp, K8-Pro-Case.dxf
"""
import re
from pathlib import Path

REPO_ROOT = Path(".")
SUPPORTED_EXTENSIONS = {".stp", ".step", ".dxf", ".dwg", ".pdf"}
NAMING_PATTERN = re.compile(
    r"^[A-Z0-9][A-Za-z0-9\s\-]+-"
    r"(Case|Plate|Encoder|Stabilizer|Keycap|Full.Model|Shell|Knob|Knob.*)"
    r"\.(stp|step|dxf|dwg|pdf)$",
    re.IGNORECASE,
)

issues = []
for path in sorted(REPO_ROOT.rglob("*")):
    if path.suffix.lower() in SUPPORTED_EXTENSIONS:
        if not NAMING_PATTERN.match(path.name):
            issues.append(str(path.relative_to(REPO_ROOT)))

if issues:
    print(f"Files with non-standard names ({len(issues)}):\n")
    for i in issues:
        print(f"  {i}")
else:
    print("All files follow naming conventions.")
```

---

### Parse STEP file metadata (no CAD software required)

```python
"""
parse_step_header.py — Extract header metadata from STEP files.
STEP files contain an ASCII header with product name, author, and schema info.
"""
import re
from pathlib import Path

def parse_step_header(filepath: Path) -> dict:
    metadata = {}
    try:
        with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
            header_lines = []
            in_header = False
            for line in f:
                if "ISO-10303-21" in line or "HEADER;" in line:
                    in_header = True
                if in_header:
                    header_lines.append(line.strip())
                if "ENDSEC;" in line and in_header:
                    break

        header_text = " ".join(header_lines)

        # FILE_DESCRIPTION
        desc_match = re.search(r"FILE_DESCRIPTION\s*\(\s*\('([^']+)'", header_text)
        if desc_match:
            metadata["description"] = desc_match.group(1)

        # FILE_NAME — product name, timestamp, author
        name_match = re.search(r"FILE_NAME\s*\(\s*'([^']+)'", header_text)
        if name_match:
            metadata["file_name"] = name_match.group(1)

        # FILE_SCHEMA
        schema_match = re.search(r"FILE_SCHEMA\s*\(\s*\('([^']+)'", header_text)
        if schema_match:
            metadata["schema"] = schema_match.group(1)

    except Exception as e:
        metadata["error"] = str(e)

    return metadata


if __name__ == "__main__":
    import sys
    target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(".")
    step_files = list(target.rglob("*.stp")) + list(target.rglob("*.step"))

    for sf in step_files[:10]:  # limit to first 10 for demo
        meta = parse_step_header(sf)
        print(f"\n{sf.name}")
        for k, v in meta.items():
            print(f"  {k}: {v}")
```

---

## Common Workflows

### Open STEP files in FreeCAD (Python API)

```python
"""
open_in_freecad.py — Open a STEP file using FreeCAD's Python API.
Requires FreeCAD to be installed and its Python path configured.
"""
import sys

# Add FreeCAD to path (adjust for your OS and FreeCAD version)
# Linux:
sys.path.append("/usr/lib/freecad/lib")
# macOS:
# sys.path.append("/Applications/FreeCAD.app/Contents/lib")

import FreeCAD
import Import  # FreeCAD's STEP importer module

def open_step(filepath: str, output_doc_name: str = "KeychronModel"):
    doc = FreeCAD.newDocument(output_doc_name)
    Import.insert(filepath, output_doc_name)
    print(f"Loaded: {filepath}")
    print(f"Objects in document: {[obj.Label for obj in doc.Objects]}")
    return doc

if __name__ == "__main__":
    step_file = "Q-Series/Q1/Q1-Case.stp"
    doc = open_step(step_file)
```

### Convert STEP to STL for 3D printing (using FreeCAD headless)

```python
"""
step_to_stl.py — Batch convert STEP files to STL for 3D printing.
Requires FreeCAD's Python bindings.
"""
import sys
from pathlib import Path

sys.path.append("/usr/lib/freecad/lib")  # adjust for your system
import FreeCAD
import Part
import Mesh

def step_to_stl(step_path: Path, stl_path: Path, tolerance: float = 0.1):
    doc = FreeCAD.newDocument("Conversion")
    Part.insert(str(step_path), "Conversion")
    shape_objects = [obj for obj in doc.Objects if hasattr(obj, "Shape")]
    if not shape_objects:
        print(f"No shape objects found in {step_path.name}")
        return False
    shape = shape_objects[0].Shape
    mesh = doc.addObject("Mesh::Feature", "Mesh")
    mesh.Mesh = Mesh.Mesh(shape.tessellate(tolerance))
    Mesh.export([mesh], str(stl_path))
    FreeCAD.closeDocument("Conversion")
    print(f"Converted: {step_path.name} -> {stl_path.name}")
    return True

if __name__ == "__main__":
    repo = Path(".")
    output_dir = Path("stl_output")
    output_dir.mkdir(exist_ok=True)
    for step_file in repo.rglob("*.stp"):
        stl_file = output_dir / (step_file.stem + ".stl")
        step_to_stl(step_file, stl_file)
```

### Using cadquery to inspect STEP geometry

```bash
pip install cadquery
```

```python
"""
inspect_step.py — Load and inspect a STEP file using CadQuery.
cadquery works without a GUI and is ideal for scripted geometry inspection.
"""
import cadquery as cq
from pathlib import Path

def inspect_step(filepath: str):
    result = cq.importers.importStep(filepath)
    bb = result.val().BoundingBox()
    print(f"File: {filepath}")
    print(f"  Bounding box (mm):")
    print(f"    X: {bb.xmin:.2f} → {bb.xmax:.2f}  (width:  {bb.xmax - bb.xmin:.2f})")
    print(f"    Y: {bb.ymin:.2f} → {bb.ymax:.2f}  (depth:  {bb.ymax - bb.ymin:.2f})")
    print(f"    Z: {bb.zmin:.2f} → {bb.zmax:.2f}  (height: {bb.zmax - bb.zmin:.2f})")
    print(f"  Faces: {result.faces().size()}")
    print(f"  Edges: {result.edges().size()}")
    return result

if __name__ == "__main__":
    import sys
    filepath = sys.argv[1] if len(sys.argv) > 1 else "Q-Series/Q1/Q1-Plate.stp"
    inspect_step(filepath)
```

### Export DXF plate dimensions summary

```python
"""
dxf_summary.py — Parse DXF files and report basic geometry stats.
"""
import ezdxf  # pip install ezdxf
from pathlib import Path

def summarize_dxf(filepath: Path):
    try:
        doc = ezdxf.readfile(str(filepath))
        msp = doc.modelspace()
        entity_counts = {}
        for entity in msp:
            t = entity.dxftype()
            entity_counts[t] = entity_counts.get(t, 0) + 1
        print(f"\n{filepath.name}")
        for etype, count in sorted(entity_counts.items()):
            print(f"  {etype}: {count}")
    except Exception as e:
        print(f"Error reading {filepath.name}: {e}")

if __name__ == "__main__":
    repo = Path(".")
    for dxf_file in sorted(repo.rglob("*.dxf")):
        summarize_dxf(dxf_file)
```

---

## 3D Printing Guidance

- **Recommended material:** PLA or PETG for prototyping cases and plates; ABS/ASA for structural parts requiring heat resistance
- **Plate files:** Use DXF for laser cutting 1.2–1.5mm steel or aluminum plates; typical MX switch cutout is 14mm × 14mm
- **Case tolerances:** Production tolerances in these files assume CNC machining; add 0.1–0.2mm clearance when 3D printing
- **Scale:** All models are in millimeters (1:1 scale). Verify scale when importing into slicers — some tools default to cm
- **Orientation:** Print cases with the inside face down to minimize support material

---

## Troubleshooting

| Problem | Solution |
|---|---|
| STEP file won't open | Ensure your CAD software supports AP214 or AP242. FreeCAD, Fusion 360, and SolidWorks all do. |
| DXF opens with wrong scale | Check units — DXF may be in mm or inches. Set your software to mm. |
| File too large to open | Use sparse checkout to get only the model you need. Large assemblies can be 50–200 MB. |
| 3D print doesn't fit | Add 0.1–0.2mm tolerance — production files are exact CNC dimensions. |
| Missing files for a model | Check the repo's open issues or `docs/repo-inventory.md`. Some models are still being uploaded. |
| Git clone is slow | Use `--filter=blob:none --sparse` (see above) to avoid downloading all binary files. |
| cadquery import error | Ensure you have `cadquery` installed: `pip install cadquery`. On Apple Silicon, use conda: `conda install -c cadquery cadquery`. |

---

## Key Reference Docs in the Repository

- `docs/file-format-guide.md` — How to open STEP, DWG, DXF, and PDF files
- `docs/getting-started.md` — First-stop guide for browsing and remixing
- `docs/3d-printing-guide.md` — Practical printing guidance
- `docs/repo-inventory.md` — Auto-generated filesystem inventory
- `docs/license-faq.md` — What you can and cannot do with these files
- `CONTRIBUTING.md` — Workflow, file standards, and submission rules

---

## License Summary

| Use | Allowed? |
|---|---|
| Personal study and learning | ✅ Yes |
| Non-commercial remixing and modding | ✅ Yes |
| Academic and educational use | ✅ Yes |
| Selling products derived from these files | ❌ No |
| Manufacturing for profit | ❌ No |
| Distribution of derivatives without attribution | ❌ No |

Full terms: see `LICENSE` and `docs/license-faq.md` in the repository.

Related Skills

open-pencil-design-editor

22
from Aradotso/trending-skills

AI-native open-source Figma alternative with CLI, MCP server, and Vue SDK for reading/writing .fig files programmatically.

llmfit-hardware-model-matcher

22
from Aradotso/trending-skills

Terminal tool that detects your hardware and recommends which LLM models will actually run well on your system

kami-document-design

22
from Aradotso/trending-skills

Design system and AI skill for generating beautiful warm-parchment documents (resumes, slides, one-pagers, portfolios, letters, white papers) with a consistent editorial aesthetic.

huashu-design-html-native

22
from Aradotso/trending-skills

HTML-native design skill for AI coding agents — high-fidelity prototypes, slides, animations, and MP4 export with 20 design philosophies and 5-dimension critique

diagram-design-editorial

22
from Aradotso/trending-skills

Generate editorial-quality HTML+SVG diagrams (13 types) for blog posts and documentation using Claude Code, with brand-matched styling and no external dependencies.

designlang-design-extract

22
from Aradotso/trending-skills

Extract complete design systems from any website — colors, typography, spacing, shadows, and more — using the designlang CLI.

cc-design-html-prototyping

22
from Aradotso/trending-skills

High-fidelity HTML design and prototype creation skill for AI coding agents — slide decks, interactive prototypes, landing pages, UI mockups, animations, and brand style cloning.

awesome-design-md-jp

22
from Aradotso/trending-skills

Create accurate Japanese UI DESIGN.md files for AI agents with proper CJK typography, font stacks, line-height, kinsoku shori, and mixed typesetting rules.

```markdown

22
from Aradotso/trending-skills

---

zeroboot-vm-sandbox

22
from Aradotso/trending-skills

Sub-millisecond VM sandboxes for AI agents using copy-on-write KVM forking via Zeroboot

yourvpndead-vpn-detection

22
from Aradotso/trending-skills

Android app that detects VPN/proxy servers (VLESS/xray/sing-box) via local SOCKS5 vulnerability, exposing exit IPs and server configs without root

xata-postgres-platform

22
from Aradotso/trending-skills

Expert skill for Xata open-source cloud-native Postgres platform with copy-on-write branching, scale-to-zero, and Kubernetes deployment