Fix lit pads not highlighting; stop swallowing haptics errors
A lit pad kept its grey text and only its border turned orange, so pads
looked unhighlighted next to every other key. Cause was cascade order,
not logic: .pad is a single-class selector like .lit, so equal
specificity meant whichever came last won, and .pad was declared after.
The state classes now sit at the bottom of the file with a comment
saying why, and a lit pad computes rgb(255,74,0) for text and border.
native.ts reported nothing when haptics failed because I wrote
catch(() => {}) and discarded the reason. It now reports once per
distinct failure - enough to read in logcat, quiet enough not to spam on
every key press.
That reporting shows the software path is fine: isNativePlatform true,
Haptics available and bridged, VIBRATE granted, and both impact and
vibrate resolve without rejecting. So the silence is below the bridge -
most likely the phone's own touch-vibration setting - and not something
this code can detect.
141 tests, typecheck clean, installed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,10 @@
|
|||||||
/* Measured geometry: 8 + 36 + 8 + 124 + 10 + 134 + 10 + 187 + 8 = 525. */
|
/* Measured geometry: 8 + 36 + 8 + 124 + 10 + 134 + 10 + 187 + 8 = 525.
|
||||||
|
*
|
||||||
|
* Ordering matters here: the state classes (.lit, .sel, .held, .down) live at
|
||||||
|
* the BOTTOM of this file. They are single-class selectors, same specificity as
|
||||||
|
* .pad or .fx, so whichever comes last wins. With .pad declared after .lit, a
|
||||||
|
* lit pad kept its grey text and only the border turned orange — pads looked
|
||||||
|
* unhighlighted next to every other key. */
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
height: var(--panel-h);
|
height: var(--panel-h);
|
||||||
@@ -16,11 +22,6 @@
|
|||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pressed, whether or not it is lit. */
|
|
||||||
.down {
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- shared key look ---- */
|
/* ---- shared key look ---- */
|
||||||
|
|
||||||
.key,
|
.key,
|
||||||
@@ -53,27 +54,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lit is an outline, not a fill. The SHIFT layer lights 35 of 54 controls, and
|
|
||||||
35 solid orange keys is a wall; solid is reserved for the selected one. */
|
|
||||||
.lit {
|
|
||||||
border-color: var(--accent);
|
|
||||||
color: var(--accent);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sel {
|
|
||||||
background: var(--accent);
|
|
||||||
border-color: var(--accent);
|
|
||||||
color: var(--on-accent);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.held {
|
|
||||||
background: var(--ink);
|
|
||||||
color: var(--page);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
@@ -102,20 +82,6 @@
|
|||||||
color: var(--pad-ink);
|
color: var(--pad-ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A round key cannot hold a lit function's label at full size: SHIFT + VALUE is
|
|
||||||
"MARK FN", which spilled outside the circle. Clip it, and shrink when lit. */
|
|
||||||
.knob,
|
|
||||||
.valueKnob {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.knob.lit,
|
|
||||||
.knob.sel,
|
|
||||||
.valueKnob.lit,
|
|
||||||
.valueKnob.sel {
|
|
||||||
font-size: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- fx and the screen ---- */
|
/* ---- fx and the screen ---- */
|
||||||
|
|
||||||
.screenRow {
|
.screenRow {
|
||||||
@@ -286,6 +252,11 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.side {
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
/* The pad's number, as printed on the hardware. */
|
/* The pad's number, as printed on the hardware. */
|
||||||
.num {
|
.num {
|
||||||
font-family: var(--mono);
|
font-family: var(--mono);
|
||||||
@@ -294,7 +265,39 @@
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.side {
|
/* ---- state: LAST in the file, so it wins over .pad, .fx, .side and .knob ---- */
|
||||||
|
|
||||||
|
/* Lit is an outline, not a fill. The SHIFT layer lights 35 of 54 controls, and
|
||||||
|
35 solid orange keys is a wall; solid is reserved for the selected one. */
|
||||||
|
.lit {
|
||||||
|
border-color: var(--accent);
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sel {
|
||||||
|
background: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--on-accent);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.held {
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--page);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A round key cannot hold a lit function's label at full size: SHIFT + VALUE is
|
||||||
|
MARK FN, which spilled outside the circle. Shrink it when lit. */
|
||||||
|
.knob.lit,
|
||||||
|
.knob.sel,
|
||||||
|
.valueKnob.lit,
|
||||||
|
.valueKnob.sel {
|
||||||
|
font-size: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pressed, whether or not it is lit. */
|
||||||
|
.down {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,18 +9,33 @@ import { Haptics, ImpactStyle, NotificationType } from "@capacitor/haptics";
|
|||||||
|
|
||||||
const isNative = (): boolean => Capacitor.isNativePlatform();
|
const isNative = (): boolean => Capacitor.isNativePlatform();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swallowing these was a mistake: haptics did nothing on the device and the
|
||||||
|
* reason had been discarded. Report once per distinct failure — enough to read
|
||||||
|
* in logcat, quiet enough not to spam on every key press.
|
||||||
|
*/
|
||||||
|
const reported = new Set<string>();
|
||||||
|
|
||||||
|
function report(where: string, error: unknown): void {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
const key = where + ":" + message;
|
||||||
|
if (reported.has(key)) return;
|
||||||
|
reported.add(key);
|
||||||
|
console.error("haptics " + where + " failed:", message);
|
||||||
|
}
|
||||||
|
|
||||||
/** A light tick under every key press, as the reference's buzz() did. */
|
/** A light tick under every key press, as the reference's buzz() did. */
|
||||||
export function tapFeedback(): void {
|
export function tapFeedback(): void {
|
||||||
if (!isNative()) return;
|
if (!isNative()) return;
|
||||||
void Haptics.impact({ style: ImpactStyle.Light }).catch(() => {});
|
Haptics.impact({ style: ImpactStyle.Light }).catch((e) => report("impact", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shaped feedback on a graded answer: right and wrong should feel different. */
|
/** Shaped feedback on a graded answer: right and wrong should feel different. */
|
||||||
export function gradeFeedback(correct: boolean): void {
|
export function gradeFeedback(correct: boolean): void {
|
||||||
if (!isNative()) return;
|
if (!isNative()) return;
|
||||||
void Haptics.notification({
|
Haptics.notification({
|
||||||
type: correct ? NotificationType.Success : NotificationType.Error,
|
type: correct ? NotificationType.Success : NotificationType.Error,
|
||||||
}).catch(() => {});
|
}).catch((e) => report("notification", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exitApp(): void {
|
export function exitApp(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user