From 9000d1f9ba51364df1444bdf6117c4c5c6bf2bb9 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 17 Aug 2026 01:09:18 +1000 Subject: [PATCH 1/3] Show Nape Pro onboard layers on the Profiles tab. --- src/app/App.tsx | 6 +- src/app/KeychronLayers.tsx | 151 +++++++++++++++++++++++++++++ src/app/cards/availability.test.ts | 19 ++++ src/app/cards/availability.ts | 3 + src/control-profiles.css | 4 +- src/control.css | 2 +- src/device/controller.ts | 85 +++++++++++++++- src/device/types.ts | 2 + src/preview-fixtures.ts | 4 +- 9 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 src/app/KeychronLayers.tsx diff --git a/src/app/App.tsx b/src/app/App.tsx index b1a389e..b255932 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -6,6 +6,7 @@ import { CaptureDialog } from "./CaptureDialog"; import { Diagnostics, LogitechDetails } from "./Diagnostics"; import { InterfaceSettings } from "./InterfaceSettings"; import { PendingBar } from "./PendingBar"; +import { KeychronLayers } from "./KeychronLayers"; import { Profiles } from "./Profiles"; import { Sidebar } from "./Sidebar"; import { Superstrike } from "./Superstrike"; @@ -155,6 +156,7 @@ function Workspace({ ].filter((node) => node !== null); const showProfiles = show(has.profiles, ["profiles"]); + const showKeychronLayers = show(has.keychronLayers, ["profiles"]); const showSuperstrike = show(has.superstrike, ["buttons"]); const showLogitechDetails = show(has.logitechDetails, ["advanced"]); const showMxMaster = on(tab, ["advanced"]) @@ -163,7 +165,8 @@ function Workspace({ const showOverview = on(tab, ["overview"]); const anyPanel = performance.length > 0 || advanced.length > 0 || lighting.length > 0 - || showProfiles || showSuperstrike || showLogitechDetails || showMxMaster || showDiagnostics || showOverview; + || showProfiles || showKeychronLayers || showSuperstrike || showLogitechDetails || showMxMaster + || showDiagnostics || showOverview; const slotsAvailable = snapshot.profile.slotsAvailable; const stagesAvailable = Boolean(status.ui?.dpiStageEditor) @@ -188,6 +191,7 @@ function Workspace({ ) : null} {showProfiles ? : null} + {showKeychronLayers ? : null} {performance.length > 0 ? (
({ + display: "flex", + gap: ".55rem", + alignItems: "center", + padding: ".45rem .6rem", + border: `1px solid ${open ? "#4a4a52" : "#26262a"}`, + borderRadius: "7px", + background: open ? "#1b1b1f" : "#141416", +}); + +const OPEN_BUTTON_STYLE: CSSProperties = { + display: "flex", + gap: ".55rem", + alignItems: "center", + flex: 1, + minWidth: 0, + textAlign: "left", + background: "none", + border: 0, + padding: 0, + cursor: "pointer", +}; + +const ICON_BUTTON_STYLE = (disabled: boolean, active = false): CSSProperties => ({ + display: "flex", + padding: ".3rem", + border: `1px solid ${active ? "#4a4a52" : "#3a3a3f"}`, + borderRadius: "5px", + background: "#19191c", + cursor: disabled ? "not-allowed" : "pointer", + opacity: disabled ? 0.4 : 1, +}); + +function layerLabel(layer: number): string { + return `Layer ${layer}`; +} + +export function KeychronLayers({ snapshot }: { snapshot: ControlSnapshot }): ReactNode { + const status = snapshot.status; + const count = status?.keychronLayerCount; + const inner = useRef(null); + const body = useRef(null); + + useEffect(() => { + if (!body.current || !inner.current) return; + body.current.style.maxHeight = snapshot.profilesExpanded ? `${inner.current.scrollHeight}px` : "0px"; + }); + + if (status == null || count == null || count < 1) return null; + + const activeLayer = status.keychronLayer ?? 1; + const editedLayer = snapshot.editedKeychronLayer ?? activeLayer; + const busy = snapshot.settingInProgress; + const tags = [ + editedLayer === activeLayer ? "active" : null, + "editing", + ].filter(Boolean).join(" · "); + + return ( +
+
+