From 0a02a32e996e3cd8ecc3fa57ffeeb6753f5dd075 Mon Sep 17 00:00:00 2001
From: AayushM0
Date: Mon, 23 Mar 2026 17:37:45 +0530
Subject: [PATCH 1/4] feat : import support both app and VIA format
---
src/app/page.tsx | 24 +++++++++++++++++++-----
src/lib/keymap.ts | 39 +++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/src/app/page.tsx b/src/app/page.tsx
index fb56386..1a2847b 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -6,7 +6,7 @@ import KeycodePicker from "@/components/KeycodePicker";
import LayerSelector from "@/components/LayerSelector";
import Toolbar from "@/components/Toolbar";
import { parseViaDefinition } from "@/lib/parser";
-import { createDefaultKeymap, exportKeymap, importKeymap, downloadJson } from "@/lib/keymap";
+import { createDefaultKeymap, exportKeymap, importAnyKeymap, downloadJson } from "@/lib/keymap";
import { ParsedLayout, Keymap, KeyboardDefinition } from "@/types/keyboard";
import defaultKb from "../../keyboards/60-percent.json";
@@ -45,23 +45,37 @@ export default function Home() {
const handleImport = useCallback(
(json: string) => {
try {
- const file = importKeymap(json);
+ const rawLayers = importAnyKeymap(json);
+ //Normalize layers length to match the current keyboard layout
+ const normalizedLayers = rawLayers.map((layer)=> {
+ const result =[...layer];
+ while(result.length < layout.keys.length){
+ result.push("KC_NO");
+ }
+
+ return result.slice(0,layout.keys.length);
+ })
+
// Pad layers to 4 if needed
- while (file.layers.length < 4) {
- file.layers.push(
+
+ while (normalizedLayers.length < 4) {
+ normalizedLayers.push(
Array.from({ length: layout.keys.length }, () => "KC_NO")
);
}
- setKeymap(file.layers);
+ setKeymap(normalizedLayers);
setSelectedKey(null);
setActiveLayer(0);
} catch (e) {
+ console.error(e);
alert("Invalid keymap file");
}
},
[layout.keys.length]
);
+
+
const handleLoadKeyboard = useCallback((json: string) => {
try {
const def = JSON.parse(json) as KeyboardDefinition;
diff --git a/src/lib/keymap.ts b/src/lib/keymap.ts
index 0aec43d..70af29a 100644
--- a/src/lib/keymap.ts
+++ b/src/lib/keymap.ts
@@ -23,6 +23,45 @@ export function exportKeymap(
return JSON.stringify(file, null, 2);
}
+/** Detect what type of JSON is being imported and then update */
+
+export function importAnyKeymap(json : string): Keymap {
+ const parsed = JSON.parse(json);
+ if(typeof parsed.version === "number" && Array.isArray(parsed.layers)){
+ validateLayers(parsed.layers);
+ return parsed.layers;
+ }
+
+ if(parsed.layers && Array.isArray(parsed.layers)){
+ validateLayers(parsed.layers);
+ return parsed.layers;
+ }
+
+ throw new Error("Invalid keymap format")
+}
+
+/** Function to validate the layers of the JSON file */
+
+function validateLayers(layers : any){
+ if(!Array.isArray(layers)){
+ throw new Error("Layers must be an array");
+ }
+
+ for(const layer of layers){
+ if(!Array.isArray(layer)){
+ throw new Error("Each layer must be an array");
+ }
+
+ for(const key of layer){
+ if(typeof key!== "string"){
+ throw new Error("Keycodes must be strings");
+ }
+ }
+
+ }
+
+}
+
/** Parse an imported keymap JSON string */
export function importKeymap(json: string): KeymapFile {
const parsed = JSON.parse(json);
From 960ff798c3a0858b1c48f01609995b2fe0b66142 Mon Sep 17 00:00:00 2001
From: AayushM0
Date: Tue, 24 Mar 2026 15:13:25 +0530
Subject: [PATCH 2/4] feat : changes to the import functionality
---
src/app/page.tsx | 22 ++---------------
src/lib/keymap.ts | 62 ++++++++++++++++++++++++++++-------------------
2 files changed, 39 insertions(+), 45 deletions(-)
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 1a2847b..ac75f35 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -45,25 +45,9 @@ export default function Home() {
const handleImport = useCallback(
(json: string) => {
try {
- const rawLayers = importAnyKeymap(json);
- //Normalize layers length to match the current keyboard layout
- const normalizedLayers = rawLayers.map((layer)=> {
- const result =[...layer];
- while(result.length < layout.keys.length){
- result.push("KC_NO");
- }
-
- return result.slice(0,layout.keys.length);
- })
+ const layers = importAnyKeymap(json, layout.keys.length);
- // Pad layers to 4 if needed
-
- while (normalizedLayers.length < 4) {
- normalizedLayers.push(
- Array.from({ length: layout.keys.length }, () => "KC_NO")
- );
- }
- setKeymap(normalizedLayers);
+ setKeymap(layers);
setSelectedKey(null);
setActiveLayer(0);
} catch (e) {
@@ -74,8 +58,6 @@ export default function Home() {
[layout.keys.length]
);
-
-
const handleLoadKeyboard = useCallback((json: string) => {
try {
const def = JSON.parse(json) as KeyboardDefinition;
diff --git a/src/lib/keymap.ts b/src/lib/keymap.ts
index 70af29a..a752fee 100644
--- a/src/lib/keymap.ts
+++ b/src/lib/keymap.ts
@@ -25,50 +25,62 @@ export function exportKeymap(
/** Detect what type of JSON is being imported and then update */
-export function importAnyKeymap(json : string): Keymap {
- const parsed = JSON.parse(json);
- if(typeof parsed.version === "number" && Array.isArray(parsed.layers)){
- validateLayers(parsed.layers);
- return parsed.layers;
+export function importAnyKeymap(
+ json: string,
+ keyCount: number
+): Keymap {
+ let parsed;
+
+ try {
+ parsed = JSON.parse(json);
+ } catch {
+ throw new Error("Invalid JSON.");
}
- if(parsed.layers && Array.isArray(parsed.layers)){
+ if (Array.isArray(parsed.layers)) {
validateLayers(parsed.layers);
- return parsed.layers;
+
+ // Normalization logic
+ const normalizedLayers: Keymap = parsed.layers.map((layer: string[]) => {
+ const result = [...layer];
+ while (result.length < keyCount) {
+ result.push("KC_NO");
+ }
+
+ return result.slice(0, keyCount);
+ })
+
+ // Ensure minimum layers
+ while (normalizedLayers.length < NUM_LAYERS) {
+ normalizedLayers.push(
+ Array.from({ length: keyCount }, () => "KC_NO")
+ );
+ }
+
+ return normalizedLayers;
}
- throw new Error("Invalid keymap format")
+ throw new Error("Invalid keymap format");
}
/** Function to validate the layers of the JSON file */
-function validateLayers(layers : any){
- if(!Array.isArray(layers)){
+function validateLayers(layers: unknown): asserts layers is Keymap {
+ if (!Array.isArray(layers)) {
throw new Error("Layers must be an array");
}
- for(const layer of layers){
- if(!Array.isArray(layer)){
+ for (const layer of layers) {
+ if (!Array.isArray(layer)) {
throw new Error("Each layer must be an array");
}
- for(const key of layer){
- if(typeof key!== "string"){
+ for (const key of layer) {
+ if (typeof key !== "string") {
throw new Error("Keycodes must be strings");
}
}
-
- }
-
-}
-
-/** Parse an imported keymap JSON string */
-export function importKeymap(json: string): KeymapFile {
- const parsed = JSON.parse(json);
- if (!parsed.layers || !Array.isArray(parsed.layers)) {
- throw new Error("Invalid keymap file: missing layers array");
}
- return parsed as KeymapFile;
}
/** Download a string as a file */
From 6ea63cc891fa9911741d036354f6d1cb3538fce3 Mon Sep 17 00:00:00 2001
From: AayushM0
Date: Tue, 24 Mar 2026 18:47:02 +0530
Subject: [PATCH 3/4] feat : Export functionality implemented for both App and
VIA format
---
src/app/page.tsx | 26 +++++++++++++++++++++-----
src/components/Toolbar.tsx | 17 +++++++++++++----
src/lib/keymap.ts | 25 +++++++++++++++++++++++++
3 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/src/app/page.tsx b/src/app/page.tsx
index ac75f35..bbe19fa 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -6,7 +6,7 @@ import KeycodePicker from "@/components/KeycodePicker";
import LayerSelector from "@/components/LayerSelector";
import Toolbar from "@/components/Toolbar";
import { parseViaDefinition } from "@/lib/parser";
-import { createDefaultKeymap, exportKeymap, importAnyKeymap, downloadJson } from "@/lib/keymap";
+import { createDefaultKeymap, exportKeymap, exportToVIA, importAnyKeymap, downloadJson } from "@/lib/keymap";
import { ParsedLayout, Keymap, KeyboardDefinition } from "@/types/keyboard";
import defaultKb from "../../keyboards/60-percent.json";
@@ -37,9 +37,24 @@ export default function Home() {
[selectedKey, activeLayer]
);
- const handleExport = useCallback(() => {
- const json = exportKeymap("My Keymap", kbName, keymap);
- downloadJson(json, "keymap.json");
+ const handleExportVIA = useCallback(() => {
+ try {
+ const json = exportToVIA(keymap);
+ downloadJson(json, "via-keymap.json");
+ } catch (e) {
+ console.error(e);
+ alert("Failed to export VIA keymap")
+ }
+ }, [keymap]);
+
+ const handleExportKeylab = useCallback(() => {
+ try {
+ const json = exportKeymap("My Keymap", kbName, keymap);
+ downloadJson(json, "kyelab-keymap.json");
+ } catch (e) {
+ console.error(e);
+ alert("Failed to export keymap")
+ }
}, [keymap, kbName]);
const handleImport = useCallback(
@@ -94,7 +109,8 @@ export default function Home() {
diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx
index 2653170..4a3104f 100644
--- a/src/components/Toolbar.tsx
+++ b/src/components/Toolbar.tsx
@@ -3,12 +3,13 @@
import { useRef } from "react";
interface Props {
- onExport: () => void;
+ onExportVIA: () => void;
+ onExportKeylab: () => void;
onImport: (json: string) => void;
onLoadKeyboard: (json: string) => void;
}
-export default function Toolbar({ onExport, onImport, onLoadKeyboard }: Props) {
+export default function Toolbar({ onExportVIA, onExportKeylab, onImport, onLoadKeyboard }: Props) {
const keymapInputRef = useRef(null);
const kbInputRef = useRef(null);
@@ -27,11 +28,19 @@ export default function Toolbar({ onExport, onImport, onLoadKeyboard }: Props) {
return (
+
+
+