diff --git a/src/lib/assets/keymaps/chara-chorder.yml b/src/lib/assets/keymaps/chara-chorder.yml index ab5688cb..225304db 100644 --- a/src/lib/assets/keymaps/chara-chorder.yml +++ b/src/lib/assets/keymaps/chara-chorder.yml @@ -104,6 +104,26 @@ actions: <<: *tertiary_keymap id: "KM_3_R" variant: right + 558: + id: HOLD_COMPOUND + title: Activate Chord Library + icon: layers + description: | + When used in a chord includes that chord as a base + compound chord for all subsequent chords. + This is effectively a library switch. + Since library activations can be nested, you + usually add a "Reset Chord Library" before this action. + 559: + id: RELEASE_COMPOUND + title: Reset Chord Library + icon: layers_clear + description: | + Releases the active compound state, returning + to the default library. + While "Activate Chord Library" can only be used + as an output of a chord, this action can be assigned + to switches directly. 576: id: ACTION_DELAY_1000 icon: clock_loader_90 diff --git a/src/lib/serial/chord.ts b/src/lib/serial/chord.ts index 959be4e3..4d52aad1 100644 --- a/src/lib/serial/chord.ts +++ b/src/lib/serial/chord.ts @@ -55,3 +55,19 @@ export function deserializeActions(native: bigint): number[] { return actions; } + +/** + * Hashes a chord input the same way as CCOS + */ +export function hashChord(actions: number[]) { + const chord = new Uint8Array(16); + const view = new DataView(chord.buffer); + const serialized = serializeActions(actions); + view.setBigUint64(0, serialized & 0xffff_ffff_ffff_ffffn, true); + view.setBigUint64(8, serialized >> 64n, true); + let hash = 2166136261; + for (let i = 0; i < 16; i++) { + hash = Math.imul(hash ^ view.getUint8(i), 16777619); + } + return hash & 0x3fff_ffff; +} diff --git a/src/lib/undo-redo.ts b/src/lib/undo-redo.ts index 413131df..f0a51f48 100644 --- a/src/lib/undo-redo.ts +++ b/src/lib/undo-redo.ts @@ -1,6 +1,6 @@ import { persistentWritable } from "$lib/storage"; import { derived } from "svelte/store"; -import type { Chord } from "$lib/serial/chord"; +import { hashChord, type Chord } from "$lib/serial/chord"; import { deviceChords, deviceLayout, @@ -158,3 +158,9 @@ export const chords = derived([overlay, deviceChords], ([overlay, chords]) => { a.localeCompare(b), ); }); + +export const chordHashes = derived( + chords, + (chords) => + new Map(chords.map((chord) => [hashChord(chord.actions), chord] as const)), +); diff --git a/src/routes/(app)/config/chords/+page.svelte b/src/routes/(app)/config/chords/+page.svelte index ee5be783..dbef7749 100644 --- a/src/routes/(app)/config/chords/+page.svelte +++ b/src/routes/(app)/config/chords/+page.svelte @@ -144,7 +144,6 @@ progress = i; if ("phrase" in chord) { - console.log(encodeChord(chord, osLayout)); await index.addAsync(i, encodeChord(chord, osLayout)); } } diff --git a/src/routes/(app)/config/chords/ChordActionEdit.svelte b/src/routes/(app)/config/chords/ChordActionEdit.svelte index beffaaca..052271c9 100644 --- a/src/routes/(app)/config/chords/ChordActionEdit.svelte +++ b/src/routes/(app)/config/chords/ChordActionEdit.svelte @@ -1,6 +1,6 @@