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 (
+ + +