mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-04-24 15:19:04 +00:00
add browser warning
This commit is contained in:
36
src/lib/assets/layouts/cc1.yml
Normal file
36
src/lib/assets/layouts/cc1.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
name: CC1
|
||||
col:
|
||||
- gap: 156
|
||||
row:
|
||||
- row:
|
||||
- { d: 30, e: 31, n: 32, w: 33, s: 34 }
|
||||
- col:
|
||||
- { d: 25, e: 26, n: 27, w: 28, s: 29 }
|
||||
- { d: 40, e: 41, n: 42, w: 43, s: 44 }
|
||||
- col:
|
||||
- { d: 20, e: 21, n: 22, w: 23, s: 24 }
|
||||
- { d: 35, e: 36, n: 37, w: 38, s: 39 }
|
||||
- { d: 15, e: 16, n: 17, w: 18, s: 19 }
|
||||
- row:
|
||||
- { d: 60, w: 61, n: 62, e: 63, s: 64 }
|
||||
- col:
|
||||
- { d: 65, w: 66, n: 67, e: 68, s: 69 }
|
||||
- { d: 80, w: 81, n: 82, e: 83, s: 84 }
|
||||
- col:
|
||||
- { d: 70, w: 71, n: 72, e: 73, s: 74 }
|
||||
- { d: 85, w: 86, n: 87, e: 88, s: 89 }
|
||||
- { d: 75, w: 76, n: 77, e: 78, s: 79 }
|
||||
- gap: 48
|
||||
margin-top: -32
|
||||
row:
|
||||
- { d: 10, e: 11, n: 12, w: 13, s: 14 }
|
||||
- { d: 55, w: 56, n: 57, e: 58, s: 59 }
|
||||
- gap: 160
|
||||
row:
|
||||
- { d: 5, e: 6, n: 7, w: 8, s: 9 }
|
||||
- { d: 50, w: 51, n: 52, e: 53, s: 54 }
|
||||
- gap: 320
|
||||
margin-top: -12
|
||||
row:
|
||||
- { d: 0, e: 1, n: 2, w: 3, s: 4 }
|
||||
- { d: 45, w: 46, n: 47, e: 48, s: 49 }
|
||||
5
src/lib/chords/bad-chord.ts
Normal file
5
src/lib/chords/bad-chord.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type {CharaLayout} from "$lib/serialization/layout"
|
||||
|
||||
export function isBadChord(chord: number[], layout: CharaLayout) {
|
||||
const adjacentHits = layout[0].
|
||||
}
|
||||
23
src/lib/chords/coverage.ts
Normal file
23
src/lib/chords/coverage.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type {Chord} from "$lib/serial/chord"
|
||||
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
|
||||
|
||||
interface Language {
|
||||
name: string
|
||||
noLazyMode?: boolean
|
||||
orderedByFrequency?: boolean
|
||||
words: string[]
|
||||
}
|
||||
|
||||
export async function calculateChordCoverage(chords: Chord[]) {
|
||||
const language: Language = await fetch("/languages/english.json").then(it => it.json())
|
||||
|
||||
const words = new Set(language.words)
|
||||
for (const chord of chords) {
|
||||
words.delete(chord.phrase.map(it => KEYMAP_CODES[it].id!).join(""))
|
||||
}
|
||||
|
||||
return {
|
||||
coverage: words.size / language.words.length,
|
||||
missing: [...words.values()],
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import RingInput from "$lib/components/RingInput.svelte"
|
||||
import layout from "$lib/assets/layouts/cc1.yml"
|
||||
|
||||
let activeLayer = 0
|
||||
</script>
|
||||
|
||||
@@ -7,17 +7,15 @@
|
||||
$: content = Array.from({length: 10}).map(() => $chords[Math.floor(Math.random() * $chords.length)])
|
||||
|
||||
let cursor = [0, 0]
|
||||
let input = []
|
||||
|
||||
$: {
|
||||
$highlightActions = content[cursor[0]]?.actions ?? []
|
||||
}
|
||||
|
||||
function keypress(event: KeyboardEvent) {
|
||||
cursor[1]++
|
||||
if (cursor[1] >= content[cursor[0]].phrase.length) {
|
||||
cursor[0]++
|
||||
cursor[1] = 0
|
||||
}
|
||||
cursor++
|
||||
input.push(event.key)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -25,15 +23,12 @@
|
||||
|
||||
<div>
|
||||
<section>
|
||||
<!-- <div class="cursor" style="translate: calc({cursor}ch - 50%) -50%" /> -->
|
||||
{#each content as word, i}
|
||||
{#if word}
|
||||
<span class="word">
|
||||
{#each word.phrase as letter, j}
|
||||
<span class="letter" class:active={i === cursor[0] && j === cursor[1]}
|
||||
>{KEYMAP_CODES[letter].id}</span
|
||||
>
|
||||
{/each}
|
||||
</span>
|
||||
{#each word.phrase as letter, j}
|
||||
<span>{KEYMAP_CODES[letter].id}</span>
|
||||
{/each}
|
||||
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -53,8 +48,11 @@
|
||||
}
|
||||
|
||||
section {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -65,15 +63,16 @@
|
||||
}
|
||||
|
||||
.cursor {
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
translate: -50% -50%;
|
||||
|
||||
width: 2px;
|
||||
height: 1em;
|
||||
|
||||
background: var(--md-sys-color-primary);
|
||||
|
||||
transition: all 250ms ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type {CharaLayout} from "$lib/serial/connection"
|
||||
import type {Chord} from "$lib/serial/chord"
|
||||
import type {CharaLayout} from "$lib/serialization/layout"
|
||||
|
||||
export interface Profile {
|
||||
name: string
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
import type {LayoutServerData} from "./$types"
|
||||
import type {RegisterSWOptions} from "vite-plugin-pwa/types"
|
||||
import {initLocalStorage} from "$lib/serial/storage"
|
||||
import {browser} from "$app/environment"
|
||||
import BrowserWarning from "./BrowserWarning.svelte"
|
||||
|
||||
export let data: LayoutServerData
|
||||
|
||||
@@ -53,6 +55,10 @@
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
{#if browser && !/Chrome\/[\d.]+(\s(?!Mobile)|$)/.test(navigator.userAgent)}
|
||||
<BrowserWarning />
|
||||
{/if}
|
||||
|
||||
<style lang="scss" global>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
||||
95
src/routes/BrowserWarning.svelte
Normal file
95
src/routes/BrowserWarning.svelte
Normal file
@@ -0,0 +1,95 @@
|
||||
<dialog open>
|
||||
<h1>Warning</h1>
|
||||
<p>
|
||||
Your current browser is not supported. Due to this site's unique requirement for serial connections, we
|
||||
require the use of <b>desktop</b> versions of <b>Chromium-based</b> browsers.
|
||||
</p>
|
||||
<p>Popular options include</p>
|
||||
<div>
|
||||
<a href="https://www.chromium.org/getting-involved/download-chromium/" target="_blank" class="chrome"
|
||||
>Chromium</a
|
||||
>
|
||||
<a href="https://www.google.com/chrome/" target="_blank" class="chrome">Chrome</a>
|
||||
<a href="https://brave.com/" target="_blank" class="brave">Brave</a>
|
||||
<a href="https://www.microsoft.com/en-us/edge/download?form=MA13FJ" target="_blank" class="edge"
|
||||
>Microsoft Edge</a
|
||||
>
|
||||
<a href="https://www.opera.com/" target="_blank" class="opera">Opera</a>
|
||||
<a href="https://vivaldi.com/download/" class="vivaldi">Vivaldi</a>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<style lang="scss">
|
||||
dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
color: var(--md-sys-color-on-error);
|
||||
|
||||
background: var(--md-sys-color-error);
|
||||
border: none;
|
||||
|
||||
> * {
|
||||
max-width: 20cm;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
color: var(--md-sys-color-on-error);
|
||||
list-style: none;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
|
||||
display: inline-block;
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
background: var(--md-sys-color-on-error);
|
||||
}
|
||||
|
||||
&.chrome::before {
|
||||
mask: url("/browsers/googlechrome.svg");
|
||||
}
|
||||
|
||||
&.brave::before {
|
||||
mask: url("/browsers/brave.svg");
|
||||
}
|
||||
|
||||
&.edge::before {
|
||||
mask: url("/browsers/microsoftedge.svg");
|
||||
}
|
||||
|
||||
&.opera::before {
|
||||
mask: url("/browsers/opera.svg");
|
||||
}
|
||||
|
||||
&.vivaldi::before {
|
||||
mask: url("/browsers/vivaldi.svg");
|
||||
}
|
||||
}
|
||||
|
||||
dialog::backdrop {
|
||||
opacity: 0.8;
|
||||
background: black;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
@@ -5,16 +5,15 @@
|
||||
import type {Index} from "flexsearch"
|
||||
import {tick} from "svelte"
|
||||
import type {Chord} from "$lib/serial/chord"
|
||||
import tippy from "tippy.js"
|
||||
import {calculateChordCoverage} from "$lib/chords/coverage"
|
||||
|
||||
$: searchIndex = $chords?.length > 0 ? buildIndex($chords) : undefined
|
||||
|
||||
function buildIndex(chords: Chord[]): Index {
|
||||
const index = new FlexSearch({tokenize: "full"})
|
||||
chords.forEach((chord, i) => {
|
||||
index.add(
|
||||
i,
|
||||
chord.phrase.map(it => KEYMAP_CODES[it].id),
|
||||
)
|
||||
index.add(i, chord.phrase.map(it => KEYMAP_CODES[it].id).join(""))
|
||||
})
|
||||
return index
|
||||
}
|
||||
@@ -29,6 +28,10 @@
|
||||
})
|
||||
}
|
||||
|
||||
function sort(event: InputEvent) {
|
||||
tippy(event.target, {})
|
||||
}
|
||||
|
||||
$: items = searchFilter?.map(it => [$chords[it], it]) ?? $chords.map((it, i) => [it, i])
|
||||
</script>
|
||||
|
||||
@@ -39,6 +42,8 @@
|
||||
{#if searchIndex}
|
||||
<input on:input={search} type="search" placeholder="Search {$chords.length} chords" />
|
||||
{/if}
|
||||
<button class="icon" on:click={sort}>sort</button>
|
||||
<button class="icon">filter</button>
|
||||
|
||||
<section>
|
||||
<table>
|
||||
@@ -62,6 +67,13 @@
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
<div>
|
||||
<p>15 Duplicate Chords</p>
|
||||
<p>12 Chords use</p>
|
||||
{#await calculateChordCoverage($chords) then { missing, coverage }}
|
||||
<p>{(coverage * 100).toFixed(1)}% of English 200</p>
|
||||
{/await}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user