Skip to content

Commit

Permalink
feat: show bigrams on the virtual keyboard
Browse files Browse the repository at this point in the history
  • Loading branch information
aradzie committed Apr 3, 2024
1 parent 6a5c4a6 commit 76e4e1d
Show file tree
Hide file tree
Showing 11 changed files with 463 additions and 21 deletions.
37 changes: 28 additions & 9 deletions packages/keybr-keyboard-ui/lib/HeatmapLayer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import test from "ava";
import TestRenderer from "react-test-renderer";
import { HeatmapLayer } from "./HeatmapLayer.tsx";

test("render empty", (t) => {
test("empty", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);

const renderer = TestRenderer.create(
Expand All @@ -15,16 +15,16 @@ test("render empty", (t) => {
t.snapshot(renderer.toJSON());
});

test("render with equal counts", (t) => {
test("equal counts", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<HeatmapLayer
histogram={[
[{ codePoint: 0x0061 }, 1],
[{ codePoint: 0x0062 }, 1],
[{ codePoint: 0x0063 }, 1],
[{ codePoint: /* a */ 0x0061 }, 1],
[{ codePoint: /* b */ 0x0062 }, 1],
[{ codePoint: /* c */ 0x0063 }, 1],
]}
modifier="f"
/>
Expand All @@ -34,16 +34,35 @@ test("render with equal counts", (t) => {
t.snapshot(renderer.toJSON());
});

test("render with different counts", (t) => {
test("different counts", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<HeatmapLayer
histogram={[
[{ codePoint: 0x0061 }, 1],
[{ codePoint: 0x0062 }, 2],
[{ codePoint: 0x0063 }, 3],
[{ codePoint: /* a */ 0x0061 }, 1],
[{ codePoint: /* b */ 0x0062 }, 2],
[{ codePoint: /* c */ 0x0063 }, 3],
]}
modifier="f"
/>
</KeyboardContext.Provider>,
);

t.snapshot(renderer.toJSON());
});

test("combine counts for the same key", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<HeatmapLayer
histogram={[
[{ codePoint: /* a */ 0x0061 }, 1],
[{ codePoint: /* A */ 0x0041 }, 1],
[{ codePoint: /* b */ 0x0062 }, 1],
]}
modifier="f"
/>
Expand Down
40 changes: 37 additions & 3 deletions packages/keybr-keyboard-ui/lib/HeatmapLayer.test.tsx.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The actual snapshot is saved in `HeatmapLayer.test.tsx.snap`.

Generated by [AVA](https://avajs.dev).

## render empty
## empty

> Snapshot 1
Expand All @@ -17,7 +17,7 @@ Generated by [AVA](https://avajs.dev).
type: 'svg',
}

## render with equal counts
## equal counts

> Snapshot 1
Expand Down Expand Up @@ -61,7 +61,7 @@ Generated by [AVA](https://avajs.dev).
type: 'svg',
}

## render with different counts
## different counts

> Snapshot 1
Expand Down Expand Up @@ -104,3 +104,37 @@ Generated by [AVA](https://avajs.dev).
},
type: 'svg',
}

## combine counts for the same key

> Snapshot 1
{
children: [
{
children: null,
props: {
className: 'spot spot_f',
cx: 269,
cy: 139,
r: 5,
},
type: 'circle',
},
{
children: null,
props: {
className: 'spot spot_f',
cx: 89,
cy: 99,
r: 25,
},
type: 'circle',
},
],
props: {
x: 15,
y: 15,
},
type: 'svg',
}
Binary file modified packages/keybr-keyboard-ui/lib/HeatmapLayer.test.tsx.snap
Binary file not shown.
19 changes: 10 additions & 9 deletions packages/keybr-keyboard-ui/lib/HeatmapLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@ export const HeatmapLayer = memo(function HeatmapLayer({
switch (modifier) {
case "h": {
const r = frequency * 15 + 5;
// top left ellipse
// Top left ellipse.
return (
<path
key={id}
className={clsx(styles.spot, styles.spot_h)}
d={`M${cx - r},${cy + r} A${r},${r} 0 0 1 ${cx + r},${cy - r}`}
d={`M ${cx - r} ${cy + r} A${r} ${r} 0 0 1 ${cx + r} ${cy - r}`}
/>
);
}
case "m": {
const r = frequency * 15 + 5;
// bottom right ellipse
// Bottom right ellipse.
return (
<path
key={id}
className={clsx(styles.spot, styles.spot_m)}
d={`M${cx - r},${cy + r} A${r},${r} 0 0 0 ${cx + r},${cy - r}`}
d={`M ${cx - r} ${cy + r} A ${r} ${r} 0 0 0 ${cx + r} ${cy - r}`}
/>
);
}
case "f": {
const r = frequency * 20 + 5;
// just circle
// Just a circle.
return (
<circle
key={id}
Expand All @@ -72,20 +72,21 @@ function normalize(
const map = new Map<KeyShape, number>();
for (const [{ codePoint }, count] of histogram) {
if (count > 0) {
const combo = keyboard.getCombo(codePoint);
if (combo != null && combo.prefix == null) {
let combo = keyboard.getCombo(codePoint);
while (combo != null) {
const shape = keyboard.getShape(combo.id);
if (shape != null) {
map.set(shape, count);
map.set(shape, (map.get(shape) ?? 0) + count);
}
combo = combo.prefix;
}
}
}
if (map.size > 0) {
const min = Math.min(...map.values());
const max = Math.max(...map.values());
return [...map]
.sort((a, b) => a[1] - b[1])
.sort(([, a], [, b]) => a - b)
.map(([shape, count]) => [
shape,
max > min ? (count - min) / (max - min) : 0.5,
Expand Down
14 changes: 14 additions & 0 deletions packages/keybr-keyboard-ui/lib/TransitionsLayer.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#marker {
/* Name only. */
}

.arrowPath {
fill: none;
stroke: #333;
stroke-width: 1px;
}

.arrowMarker {
fill: #333;
stroke: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const marker: string;
export const arrowPath: string;
export const arrowMarker: string;
61 changes: 61 additions & 0 deletions packages/keybr-keyboard-ui/lib/TransitionsLayer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { KeyboardContext, Layout, loadKeyboard, Ngram2 } from "@keybr/keyboard";
import test from "ava";
import TestRenderer from "react-test-renderer";
import { TransitionsLayer } from "./TransitionsLayer.tsx";

test("empty", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);
const ngram = new Ngram2([/* a */ 0x0061, /* b */ 0x0062, /* c */ 0x0063]);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<TransitionsLayer ngram={ngram} />
</KeyboardContext.Provider>,
);

t.snapshot(renderer.toJSON());
});

test("equal counts", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);
const ngram = new Ngram2([/* a */ 0x0061, /* b */ 0x0062, /* c */ 0x0063]);
ngram.set(/* a */ 0x0061, /* b */ 0x0062, 1);
ngram.set(/* b */ 0x0062, /* c */ 0x0063, 1);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<TransitionsLayer ngram={ngram} />
</KeyboardContext.Provider>,
);

t.snapshot(renderer.toJSON());
});

test("different counts", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);
const ngram = new Ngram2([/* a */ 0x0061, /* b */ 0x0062, /* c */ 0x0063]);
ngram.set(/* a */ 0x0061, /* b */ 0x0062, 1);
ngram.set(/* b */ 0x0062, /* c */ 0x0063, 2);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<TransitionsLayer ngram={ngram} />
</KeyboardContext.Provider>,
);

t.snapshot(renderer.toJSON());
});

test("self arrow", (t) => {
const keyboard = loadKeyboard(Layout.EN_US);
const ngram = new Ngram2([/* a */ 0x0061, /* b */ 0x0062, /* c */ 0x0063]);
ngram.set(/* a */ 0x0061, /* a */ 0x0061, 1);

const renderer = TestRenderer.create(
<KeyboardContext.Provider value={keyboard}>
<TransitionsLayer ngram={ngram} />
</KeyboardContext.Provider>,
);

t.snapshot(renderer.toJSON());
});
Loading

0 comments on commit 76e4e1d

Please sign in to comment.