From bf40ddc6ead8d66835ee8c7a19f2fec8a91a4ffe Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Tue, 8 Oct 2024 16:02:55 -0600 Subject: [PATCH] feat: Improved key rendering for HID usages. * Have short/med/long override labels for HID usages, and use CSS container queries to select the right one for display based on key sizing. --- package-lock.json | 16 +++ package.json | 1 + src/hid-usage-name-overrides.json | 155 ++++++++++++------------ src/hid-usages.ts | 20 ++- src/keyboard/HidUsageLabel.tsx | 32 +++++ src/keyboard/Key.stories.tsx | 10 +- src/keyboard/Key.tsx | 6 +- src/keyboard/Keymap.tsx | 24 ++-- src/keyboard/PhysicalLayout.stories.tsx | 50 +++++--- tailwind.config.js | 3 +- 10 files changed, 202 insertions(+), 115 deletions(-) create mode 100644 src/keyboard/HidUsageLabel.tsx diff --git a/package-lock.json b/package-lock.json index 05e686c..069a5ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "zmk-studio", "version": "0.0.3", "dependencies": { + "@tailwindcss/container-queries": "^0.1.1", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-cli": "^2.0.0", "@zmkfirmware/zmk-studio-ts-client": "^0.0.18", @@ -6083,6 +6084,15 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@tailwindcss/container-queries": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz", + "integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.2.0" + } + }, "node_modules/@tauri-apps/api": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.1.tgz", @@ -18020,6 +18030,12 @@ "@swc/counter": "^0.1.3" } }, + "@tailwindcss/container-queries": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz", + "integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==", + "requires": {} + }, "@tauri-apps/api": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.1.tgz", diff --git a/package.json b/package.json index 7665f05..337cbc2 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "@tailwindcss/container-queries": "^0.1.1", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-cli": "^2.0.0", "@zmkfirmware/zmk-studio-ts-client": "^0.0.18", diff --git a/src/hid-usage-name-overrides.json b/src/hid-usage-name-overrides.json index a1fdda5..f2773ff 100644 --- a/src/hid-usage-name-overrides.json +++ b/src/hid-usage-name-overrides.json @@ -1,82 +1,85 @@ { "7": { - "30": "1", - "31": "2", - "32": "3", - "33": "4", - "34": "5", - "35": "6", - "36": "7", - "37": "8", - "38": "9", - "39": "0", - "40": "Ret", - "41": "Esc", - "42": "BSp", - "44": "␣", - "45": "-", - "46": "=", - "47": "{", - "48": "}", - "49": "\\", - "50": "NUHS", - "51": ";", - "52": "'", - "53": "`", - "54": ",", - "55": ".", - "56": "/", - "57": "Cap", - "70": "PrSc", - "71": "ScLk", - "73": "Ins", - "75": "PgUp", - "76": "Del", - "78": "PgDn", - "79": "→", - "80": "←", - "81": "↓", - "82": "↑", - "83": "Num", - "84": "/", - "85": "*", - "86": "-", - "87": "+", - "88": "Enter", - "89": "1 End", - "90": "2 ↓", - "91": "3 PgDn", - "92": "4 ←", - "93": "5", - "94": "6 →", - "95": "7 Home", - "96": "8 ↑", - "97": "9 PgUp", - "98": "0 Ins", - "99": ". Del", - "100": "NUBS", - "103": "=", - "133": ",", - "134": "=", - "176": "00", - "177": "000", - "224": "Ctrl", - "225": "Shift", - "226": "Alt", - "227": "GUI", - "228": "Ctrl", - "229": "Shift", - "230": "AltGr", - "231": "GUI" + "30": { "short": "1" }, + "31": { "short": "2" }, + "32": { "short": "3" }, + "33": { "short": "4" }, + "34": { "short": "5" }, + "35": { "short": "6" }, + "36": { "short": "7" }, + "37": { "short": "8" }, + "38": { "short": "9" }, + "39": { "short": "0" }, + "40": { "short": "Ret", "med": "Return" }, + "41": { "short": "Esc", "long": "Escape" }, + "42": { "short": "BkSp", "med": "BkSpc", "long": "Backspace" }, + "44": { "short": "␣", "med": "Space" }, + "45": { "short": "-", "med": "Dash" }, + "46": { "short": "=", "med": "Equals" }, + "47": { "short": "{" }, + "48": { "short": "}" }, + "49": { "short": "\\" }, + "50": { "short": "NUHS", "long": "NonUS Hash" }, + "51": { "short": ";" }, + "52": { "short": "'" }, + "53": { "short": "`" }, + "54": { "short": "," }, + "55": { "short": "." }, + "56": { "short": "/" }, + "57": { "short": "Cap", "long": "Caps Lock" }, + "70": { "short": "PrSc", "long": "Print Scr" }, + "71": { "short": "ScLk", "long": "ScrollLock" }, + "72": { "short": "Paus", "med": "Pause" }, + "73": { "short": "Ins", "med": "Insert" }, + "75": { "short": "PgUp", "med": "PageUp", "long": "Page Up" }, + "76": { "short": "Del", "med": "Delete" }, + "78": { "short": "PgDn", "med": "PageDn", "long": "Page Down" }, + "79": { "short": "→" }, + "80": { "short": "←" }, + "81": { "short": "↓" }, + "82": { "short": "↑" }, + "83": { "short": "Num", "med": "NumLck", "long": "Num Lock" }, + "84": { "short": "/" }, + "85": { "short": "*" }, + "86": { "short": "-" }, + "87": { "short": "+" }, + "88": { "short": "Ent", "med": "KP Ent", "long": "KP Enter" }, + "89": { "short": "1 En", "med": "1 End" }, + "90": { "short": "2 ↓" }, + "91": { "short": "3 PD", "med": "3 PgDn" }, + "92": { "short": "4 ←" }, + "93": { "short": "5" }, + "94": { "short": "6 →" }, + "95": { "short": "7 Hm", "med": "7 Home" }, + "96": { "short": "8 ↑" }, + "97": { "short": "9 PU", "med": "9 PgUp" }, + "98": { "short": "0 In", "med": "0 Ins", "long": "0 Insert" }, + "99": { "short": ". Dl", "med": ". Del", "long": ". Delete" }, + "101": { "short": "Appl", "med": "Appl", "long": "Applicat'n" }, + "102": { "short": "Power", "med": "Power" }, + "100": { "short": "NUBS" }, + "103": { "short": "=" }, + "133": { "short": "," }, + "134": { "short": "=" }, + "176": { "short": "00" }, + "177": { "short": "000" }, + "224": { "short": "Ctrl", "med": "L Ctrl" }, + "225": { "short": "Shft", "med": "L Shft", "long": "L Shift" }, + "226": { "short": "Alt", "med": "L Alt", "long": "Left Alt" }, + "227": { "short": "GUI", "med": "L GUI", "long": "Left GUI" }, + "228": { "short": "Ctrl", "med": "R Ctrl" }, + "229": { "short": "Shft", "med": "R Shft", "long": "R Shift" }, + "230": { "short": "AltG", "med": "AltGr" }, + "231": { "short": "GUI", "med": "R GUI", "long": "Right GUI" } }, "12": { - "111": "🔆", - "112": "🔅", - "181": "⇥", - "182": "⇤", - "205": "⏯️", - "226": "🔇", - "233": "🔊", - "234": "🔉" + "111": { "short": "🔆" }, + "112": { "short": "🔅" }, + "181": { "short": "⇥" }, + "182": { "short": "⇤" }, + "205": { "short": "⏯️" }, + "226": { "short": "🔇" }, + "233": { "short": "🔊" }, + "234": { "short": "🔉" } } } diff --git a/src/hid-usages.ts b/src/hid-usages.ts index 41d9233..a3e3dd8 100644 --- a/src/hid-usages.ts +++ b/src/hid-usages.ts @@ -3,7 +3,13 @@ import { UsagePages } from "./keyboard-and-consumer-usage-tables.json"; import HidOverrides from "./hid-usage-name-overrides.json"; -const overrides: Record> = HidOverrides; +interface HidLabels { + short?: string; + med?: string; + long?: string; +} + +const overrides: Record> = HidOverrides; export interface UsageId { Id: number; @@ -30,7 +36,17 @@ export const hid_usage_get_label = ( usage_page: number, usage_id: number ): string | undefined => - overrides[usage_page.toString()]?.[usage_id.toString()] || + overrides[usage_page.toString()]?.[usage_id.toString()]?.short || UsagePages.find((p) => p.Id === usage_page)?.UsageIds?.find( (u) => u.Id === usage_id )?.Name; + +export const hid_usage_get_labels = ( + usage_page: number, + usage_id: number +): { short?: string; med?: string; long?: string } => + overrides[usage_page.toString()]?.[usage_id.toString()] || { + short: UsagePages.find((p) => p.Id === usage_page)?.UsageIds?.find( + (u) => u.Id === usage_id + )?.Name, + }; diff --git a/src/keyboard/HidUsageLabel.tsx b/src/keyboard/HidUsageLabel.tsx new file mode 100644 index 0000000..aa15195 --- /dev/null +++ b/src/keyboard/HidUsageLabel.tsx @@ -0,0 +1,32 @@ +import { + hid_usage_get_labels, + hid_usage_page_and_id_from_usage, +} from "../hid-usages"; + +export interface HidUsageLabelProps { + hid_usage: number; +} + +function remove_prefix(s?: string) { + return s?.replace(/^Keyboard /, ""); +} + +export const HidUsageLabel = ({ hid_usage }: HidUsageLabelProps) => { + let [page, id] = hid_usage_page_and_id_from_usage(hid_usage); + + // TODO: Do something with implicit mods! + page &= 0xff; + + let labels = hid_usage_get_labels(page, id); + + return ( + + ); +}; diff --git a/src/keyboard/Key.stories.tsx b/src/keyboard/Key.stories.tsx index 2b81a38..0772a5a 100644 --- a/src/keyboard/Key.stories.tsx +++ b/src/keyboard/Key.stories.tsx @@ -29,7 +29,13 @@ export const Normal: Story = { width: 1, height: 1, header: "Key Press", - children: [A], + children: [ + , + ], }, }; @@ -39,7 +45,7 @@ export const Selected: Story = { width: 1, height: 1, header: "Key Press", - children: [B], + children: [B], }, }; diff --git a/src/keyboard/Key.tsx b/src/keyboard/Key.tsx index fb270c5..80babd4 100644 --- a/src/keyboard/Key.tsx +++ b/src/keyboard/Key.tsx @@ -56,7 +56,7 @@ export const Key = ({ const children = Children.map(props.children, (c) => (
{c}
@@ -74,10 +74,10 @@ export const Key = ({ data-zoomer={hoverZoom} className={`rounded${ oneU > 20 ? "-md" : "" - } transition-all duration-100 m-auto p-0 b-0 box-border grid grid-rows-[0_var(--zmk-key-center-height)_0] grid-cols-[0_var(--zmk-key-center-width)_0] data-[zoomer=true]:hover:grid-rows-[1em_var(--zmk-key-center-height)_1em] data-[zoomer=true]:hover:grid-cols-[1em_var(--zmk-key-center-width)_1em] shadow-[0_0_0_1px_inset] shadow-base-content data-[zoomer=true]:shadow-base-200 data-[zoomer=true]:hover:shadow-base-content data-[zoomer=true]:hover:z-50 text-base-content bg-base-100 aria-selected:bg-primary aria-selected:text-primary-content`} + } transition-all duration-100 m-auto p-0 b-0 box-border grid grid-rows-[0_var(--zmk-key-center-height)_0] grid-cols-[0_var(--zmk-key-center-width)_0] data-[zoomer=true]:hover:grid-rows-[1em_var(--zmk-key-center-height)_1em] data-[zoomer=true]:hover:grid-cols-[1em_var(--zmk-key-center-width)_1em] shadow-[0_0_0_1px_inset] shadow-base-content data-[zoomer=true]:shadow-base-200 data-[zoomer=true]:hover:shadow-base-content data-[zoomer=true]:hover:z-50 text-base-content bg-base-100 aria-selected:bg-primary aria-selected:text-primary-content grow @container`} > {header && ( - + {header} )} diff --git a/src/keyboard/Keymap.tsx b/src/keyboard/Keymap.tsx index 947fdbf..4d457b1 100644 --- a/src/keyboard/Keymap.tsx +++ b/src/keyboard/Keymap.tsx @@ -5,11 +5,10 @@ import { import type { GetBehaviorDetailsResponse } from "@zmkfirmware/zmk-studio-ts-client/behaviors"; import { - hid_usage_get_label, - hid_usage_page_and_id_from_usage, -} from "../hid-usages"; - -import { LayoutZoom, PhysicalLayout as PhysicalLayoutComp } from "./PhysicalLayout"; + LayoutZoom, + PhysicalLayout as PhysicalLayoutComp, +} from "./PhysicalLayout"; +import { HidUsageLabel } from "./HidUsageLabel"; type BehaviorMap = Record; @@ -48,15 +47,6 @@ export const Keymap = ({ }; } - let [page, id] = hid_usage_page_and_id_from_usage( - keymap.layers[selectedLayerIndex].bindings[i].param1 - ); - - // TODO: Do something with implicit mods! - page &= 0xff; - - let label = hid_usage_get_label(page, id)?.replace(/^Keyboard /, ""); - return { header: behaviors[keymap.layers[selectedLayerIndex].bindings[i].behaviorId] @@ -68,7 +58,11 @@ export const Keymap = ({ r: (k.r || 0) / 100.0, rx: (k.rx || 0) / 100.0, ry: (k.ry || 0) / 100.0, - children: {label}, + children: ( + + ), }; }); diff --git a/src/keyboard/PhysicalLayout.stories.tsx b/src/keyboard/PhysicalLayout.stories.tsx index 37dd913..fa1b2f0 100644 --- a/src/keyboard/PhysicalLayout.stories.tsx +++ b/src/keyboard/PhysicalLayout.stories.tsx @@ -1,6 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react"; import { fn } from "@storybook/test"; import { PhysicalLayout } from "./PhysicalLayout"; +import { HidUsageLabel } from "./HidUsageLabel"; +import { hid_usage_from_page_and_id } from "../hid-usages"; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export const meta = { @@ -21,9 +23,15 @@ const meta = { export default meta; type Story = StoryObj; -const TOP = ["Esc", ..."QWERTYUIOP"]; -const MIDDLE = [..."ASDFGHJKL;"]; -const LOWER = [..."ZXCVBNM<>", "Up", "Shift"]; +const TOP = [41, ...[..."QWERTYUIOP"].map((c) => c.charCodeAt(0) - 61)]; +const MIDDLE = [...[..."ASDFGHJKL"].map((c) => c.charCodeAt(0) - 61), 51]; +const LOWER = [ + ...[..."ZXCVBNM"].map((c) => c.charCodeAt(0) - 61), + 54, + 55, + 82, + 229, +]; const MINIVAN_POSITIONS = [ ...TOP.map((k, i) => ({ @@ -32,7 +40,7 @@ const MINIVAN_POSITIONS = [ x: i, y: 0, header: "Key Press", - children: [{k}], + children: [], })), { x: TOP.length, @@ -40,7 +48,7 @@ const MINIVAN_POSITIONS = [ width: 1.75, height: 1, header: "Key Press", - children: [Backspace], + children: [], }, { x: 0, @@ -56,7 +64,7 @@ const MINIVAN_POSITIONS = [ width: 1, height: 1, header: "Key Press", - children: [{k}], + children: [], })), { x: MIDDLE.length + 1.25, @@ -64,7 +72,7 @@ const MINIVAN_POSITIONS = [ width: 1.5, height: 1, header: "Key Press", - children: [Enter], + children: [], }, { x: 0, @@ -72,7 +80,9 @@ const MINIVAN_POSITIONS = [ width: 1.75, height: 1, header: "Key Press", - children: [Shift], + children: [ + , + ], }, ...LOWER.map((k, i) => ({ x: i + 1.75, @@ -80,7 +90,7 @@ const MINIVAN_POSITIONS = [ width: 1, height: 1, header: "Key Press", - children: [{k}], + children: [], })), { x: 0, @@ -88,7 +98,9 @@ const MINIVAN_POSITIONS = [ width: 1.25, height: 1, header: "Key Press", - children: [Control], + children: [ + , + ], }, { x: 1.25, @@ -96,7 +108,9 @@ const MINIVAN_POSITIONS = [ width: 1.5, height: 1, header: "Key Press", - children: [Code], + children: [ + , + ], }, { x: 2.75, @@ -104,7 +118,9 @@ const MINIVAN_POSITIONS = [ width: 1.25, height: 1, header: "Key Press", - children: [Alt], + children: [ + , + ], }, { x: 4, @@ -128,7 +144,9 @@ const MINIVAN_POSITIONS = [ width: 1.5, height: 1, header: "Key Press", - children: [Alt], + children: [ + , + ], }, { x: 9.75, @@ -136,7 +154,7 @@ const MINIVAN_POSITIONS = [ width: 1, height: 1, header: "Key Press", - children: [Left], + children: [], }, { x: 10.75, @@ -144,7 +162,7 @@ const MINIVAN_POSITIONS = [ width: 1, height: 1, header: "Key Press", - children: [Down], + children: [], }, { x: 11.75, @@ -152,7 +170,7 @@ const MINIVAN_POSITIONS = [ width: 1, height: 1, header: "Key Press", - children: [Right], + children: [], }, ]; diff --git a/tailwind.config.js b/tailwind.config.js index 99b00a7..2f14d26 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,5 +1,6 @@ /** @type {import('tailwindcss').Config} */ import trac from "tailwindcss-react-aria-components"; +import contQueries from "@tailwindcss/container-queries"; export default { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], @@ -28,5 +29,5 @@ export default { keycap: ["Inter", "system-ui"], }, }, - plugins: [trac({ prefix: "rac" })], + plugins: [contQueries, trac({ prefix: "rac" })], };