From 7279e8f72cbf38c3357e420b44780ca3d28de628 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 23 Oct 2026 13:31:43 +0000
Subject: [PATCH 1/3] chore(protocol): bump @openmouse/protocol to 0.46.0
---
package-lock.json | 8 ++++----
package.json | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 43e1251d..add7aac7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"@openmouse/keyboard-protocol": "1.1.0",
- "@openmouse/protocol": "0.45.2",
+ "@openmouse/protocol": "0.46.0",
"lucide-react": "^1.43.0",
"posthog-js": "^1.438.17",
"preact": "^10.29.8"
@@ -492,9 +492,9 @@
}
},
"node_modules/@openmouse/protocol": {
- "version": "0.45.2",
- "resolved": "https://registry.npmjs.org/@openmouse/protocol/-/protocol-0.45.2.tgz",
- "integrity": "sha512-971Aihm8fJOuRHxNvhEPKYpWe5BM4v9GO8bahHKTxGa4nMrtKYd46A9gPKDqIZk0T+mnyjfBloH1T4vum5Qzag==",
+ "version": "0.46.0",
+ "resolved": "https://registry.npmjs.org/@openmouse/protocol/-/protocol-0.46.0.tgz",
+ "integrity": "sha512-7/llEdOV4tslecHd39h9ZD+YfgqvV/vJie1F012GD0+Pc82Vh+41Z461lJDeO8cHFG3VnqYP52OxPqJM4Xe6ZQ==",
"license": "AGPL-3.0-or-later",
"engines": {
"node": ">=20"
diff --git a/package.json b/package.json
index 52889f6e..b9917100 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
},
"dependencies": {
"@openmouse/keyboard-protocol": "1.1.0",
- "@openmouse/protocol": "0.45.2",
+ "@openmouse/protocol": "0.46.0",
"lucide-react": "^1.43.0",
"posthog-js": "^1.438.17",
"preact": "^10.29.8"
From 294783338fd05cc3b61cffbbd4a8ad0066ecd124 Mon Sep 17 00:00:00 2001
From: m4milaad
Date: Fri, 23 Oct 2026 19:17:38 +0530
Subject: [PATCH 2/3] feat(devices): render driver status note and battery hint
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Drivers already declare two shell hints in MouseUiHints that nothing
rendered: `statusNote` (an extra sentence for the connected status line,
explained why a connection is limited) and `forceShowBattery` (show the
battery column even when the percent is unknown). The Cosmic Byte Kilonova
3335IC driver in OpenMouse-Project/mouse-protocol#249 is the first one that
relies on both, so with the current shell its page showed the device and
nothing else.
- The hero's connected status line now appends `status.ui.statusNote` after
the connection detail.
- `forceShowBattery` now keeps the battery present in the hero stats and the
current-setup figures, shown as "–" while the percent is unknown, instead
of only when a percent exists. Wired devices that report no percent until
they flunk a battery read (Endgame Gear OP1 V2 fixtures already set the
hint) gain the same column.
- Adds a `?preview=kilonova` fixture mirroring the driver's browser status so
the composition is visible without the hardware.
---
src/device/preview/preview-fixtures.ts | 31 ++++++++++++++++++++++++++
src/device/preview/preview-modes.ts | 2 ++
src/features/devices/DeviceHero.tsx | 4 ++++
src/features/devices/DeviceStats.tsx | 5 +++++
4 files changed, 42 insertions(+)
diff --git a/src/device/preview/preview-fixtures.ts b/src/device/preview/preview-fixtures.ts
index b0d74d87..57f384fe 100644
--- a/src/device/preview/preview-fixtures.ts
+++ b/src/device/preview/preview-fixtures.ts
@@ -343,6 +343,36 @@ const ATTACKSHARK: MouseStatus = {
firmware: ["Mouse 1.0.4", "Dongle 1.0.3"],
};
+/**
+ * The Cosmic Byte Kilonova 3335IC through its receiver (SinoWealth
+ * `258a:002f`), browser transport: identity and connection only, with battery
+ * and link waiting on OpenMouse Bridge to claim report 5. The driver refuses
+ * writes until the bank is decoded, so the shell shows the device, the note,
+ * and no settings cards.
+ */
+const KILONOVA: MouseStatus = {
+ brand: "Cosmic Byte" as MouseStatus["brand"],
+ name: "Cosmic Byte Kilonova 3335IC",
+ ui: {
+ family: "sinowealth-kilonova-3335ic",
+ settingsReady: false,
+ valuesVerified: false,
+ forceShowBattery: true,
+ hideProcessingCard: true,
+ defaultDisplayName: "Cosmic Byte Kilonova 3335IC",
+ statusNote: "OpenMouse recognizes this receiver as a Cosmic Byte Kilonova 3335IC and leaves its settings untouched: the settings layout is not decoded yet. The battery and awake check need OpenMouse Bridge.",
+ },
+ batteryPercent: null,
+ batteryState: "Unknown",
+ dpi: 0,
+ pollingRateHz: 0,
+ activeProfile: null,
+ connectionType: "Wireless",
+ connectionDetail: "2.4 GHz receiver",
+ liftOffDistance: null,
+ firmware: [],
+};
+
const M3K: MouseStatus = {
brand: "Zaunkoenig" as MouseStatus["brand"],
name: "Zaunkoenig M3K",
@@ -1086,6 +1116,7 @@ export const PREVIEW_FIXTURES: Record = {
wlmouse: { label: "WLMouse BEAST X", status: WLMOUSE },
lamzu: { label: "Lamzu Atlantis OG V2", status: LAMZU },
"attack-shark": { label: "Attack Shark R5 Ultra", status: ATTACKSHARK },
+ kilonova: { label: "Cosmic Byte Kilonova 3335IC", status: KILONOVA },
crdrako: { label: "CRDRAKO KO-ONE", status: CRDRAKO },
m3k: { label: "Zaunkoenig M3K", status: M3K },
atk: { label: "ATK A9 Ultra", status: ATK },
diff --git a/src/device/preview/preview-modes.ts b/src/device/preview/preview-modes.ts
index fe185d70..69e5db06 100644
--- a/src/device/preview/preview-modes.ts
+++ b/src/device/preview/preview-modes.ts
@@ -20,6 +20,7 @@ export const PREVIEW_KEYS = [
"wlmouse",
"lamzu",
"attack-shark",
+ "kilonova",
"crdrako",
"m3k",
"atk",
@@ -70,6 +71,7 @@ export function parsePreviewMode(value: string | null): PreviewMode | null {
case "wlmouse": return "wlmouse";
case "lamzu": return "lamzu";
case "attack-shark": return "attack-shark";
+ case "kilonova": return "kilonova";
case "crdrako": return "crdrako";
case "m3k": return "m3k";
case "atk": return "atk";
diff --git a/src/features/devices/DeviceHero.tsx b/src/features/devices/DeviceHero.tsx
index 0fecfaea..be48690f 100644
--- a/src/features/devices/DeviceHero.tsx
+++ b/src/features/devices/DeviceHero.tsx
@@ -30,6 +30,7 @@ export function DeviceHero({
const activeEntry = snapshot.devices.find((device) => device.selected) ?? null;
const name = status?.name ?? keyboard?.name ?? activeEntry?.name ?? "";
const brand = status ? noirBrandLabel(status) : null;
+ const statusNote = status?.ui?.statusNote ?? null;
const image = snapshot.deviceArtwork
?? (keyboard ? deviceImage({ vendorId: keyboard.vendorId, productId: keyboard.productId } as HIDDevice, keyboard.name) : null);
const needsArtwork = Boolean(status && active && (imageFailed || isUnknownDevice(
@@ -82,6 +83,7 @@ export function DeviceHero({
{brand ? <>·{displayText(locale, brand)}> : null}
{status?.connectionDetail ? <>·{displayText(locale, status.connectionDetail)}> : null}
{activeEntry?.transport === "bridge" ? <>·{t(locale, "bridge.title")}> : null}
+ {statusNote ? <>·{statusNote}> : null}
{status ? : null}
@@ -152,6 +154,8 @@ export function CurrentSetupPanel({ snapshot }: { snapshot: ControlSnapshot }):
unit: "%",
sub: displayText(locale, status.batteryState),
});
+ } else if (status.ui?.forceShowBattery === true) {
+ figures.push({ key: "battery", label: t(locale, "ov.batteryLabel"), value: "–" });
}
if (status.activeProfile != null) {
figures.push({ key: "profile", label: t(locale, "ov.onboardProfile"), value: String(status.activeProfile) });
diff --git a/src/features/devices/DeviceStats.tsx b/src/features/devices/DeviceStats.tsx
index 1552cf09..cc77b77d 100644
--- a/src/features/devices/DeviceStats.tsx
+++ b/src/features/devices/DeviceStats.tsx
@@ -41,6 +41,11 @@ export function DeviceStats({
{status.batteryPercent}%
{estimate && full ? {estimate.time} : null}
+ ) : status.ui?.forceShowBattery === true ? (
+
+
+ {"–"}
+
) : null}
{status.pollingRateHz > 0 ? (
From b631b0bbaa62d23b4a8fe17472c63837b377aa28 Mon Sep 17 00:00:00 2001
From: m4milaad
Date: Sat, 24 Oct 2026 17:50:46 +0530
Subject: [PATCH 3/3] fix(preview): update Kilonova fixture to show decoded DPI
---
src/device/preview/preview-fixtures.ts | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/device/preview/preview-fixtures.ts b/src/device/preview/preview-fixtures.ts
index 57f384fe..a30d5061 100644
--- a/src/device/preview/preview-fixtures.ts
+++ b/src/device/preview/preview-fixtures.ts
@@ -345,10 +345,9 @@ const ATTACKSHARK: MouseStatus = {
/**
* The Cosmic Byte Kilonova 3335IC through its receiver (SinoWealth
- * `258a:002f`), browser transport: identity and connection only, with battery
- * and link waiting on OpenMouse Bridge to claim report 5. The driver refuses
- * writes until the bank is decoded, so the shell shows the device, the note,
- * and no settings cards.
+ * `258a:002f`), browser transport: identity, battery, link, and read-only DPI.
+ * The driver refuses writes until the full bank is decoded, so the shell shows
+ * DPI read-out but no settings cards.
*/
const KILONOVA: MouseStatus = {
brand: "Cosmic Byte" as MouseStatus["brand"],
@@ -356,15 +355,17 @@ const KILONOVA: MouseStatus = {
ui: {
family: "sinowealth-kilonova-3335ic",
settingsReady: false,
- valuesVerified: false,
+ valuesVerified: true,
forceShowBattery: true,
hideProcessingCard: true,
defaultDisplayName: "Cosmic Byte Kilonova 3335IC",
- statusNote: "OpenMouse recognizes this receiver as a Cosmic Byte Kilonova 3335IC and leaves its settings untouched: the settings layout is not decoded yet. The battery and awake check need OpenMouse Bridge.",
+ statusNote: "OpenMouse reads this Cosmic Byte Kilonova 3335IC's DPI from its onboard memory; other settings are not decoded and changes are refused. The battery and awake check need OpenMouse Bridge.",
},
batteryPercent: null,
batteryState: "Unknown",
- dpi: 0,
+ dpi: 1600,
+ dpiStages: [400, 800, 1600, 3200, 6400, 16000],
+ activeDpiStage: 2,
pollingRateHz: 0,
activeProfile: null,
connectionType: "Wireless",