initial commit

This commit is contained in:
2023-07-05 15:22:14 +02:00
commit e339e924dc
38 changed files with 10057 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
<script>
import {serialPort} from "$lib/serial/connection.js"
import {browser} from "$app/environment"
</script>
<nav>
<h1>dot i/o</h1>
<div class="steps">
<a href="/train/cpm" title="CPM - characters per minute" class="icon train cpm">music_note</a>
<a href="/train/chm" title="ChM - chords mastered" class="icon train chords">piano</a>
<a href="/train/avg-wpm" title="aWPM - average words per minute" class="icon test avg">avg_pace</a>
<a href="/train/stm" title="StM - sentences mastered" class="icon train sentences">lyrics</a>
<a href="/train/top-wpm" title="tWPM - top words per minute" class="icon test top">speed</a>
<a href="/train/cm" title="CM - concepts mastered" class="icon train concepts">cognition</a>
</div>
<div class="actions">
{#if browser && !("serial" in navigator)}
<abbr
title="Your browser does not support serial connections. Try using Chrome instead."
class="icon error"
>
warning
</abbr>
{/if}
<a
href="/device-manager"
title="Device Manager"
class="icon connect"
class:error={$serialPort === undefined}>cable</a
>
<a href="/" title="Statistics" class="icon account">person</a>
</div>
</nav>
<style lang="scss">
nav {
display: flex;
gap: 4px;
align-items: center;
justify-content: space-between;
width: 100%;
}
h1 {
margin-block: 0;
font-size: 1.5rem;
}
.icon {
cursor: pointer;
aspect-ratio: 1;
padding: 4px;
color: inherit;
text-decoration: none;
background: transparent;
border: none;
border-radius: 50%;
&.error {
color: var(--md-sys-color-on-error);
background: var(--md-sys-color-error);
}
&:hover {
color: var(--md-sys-color-on-primary);
background: var(--md-sys-color-primary);
}
}
.steps {
display: flex;
> a.icon {
aspect-ratio: unset;
margin-inline: -4px;
padding-inline: 16px;
font-size: 24px;
color: var(--md-sys-on-surface-variant);
background: var(--md-sys-color-surface-variant);
clip-path: polygon(25% 50%, 0% 0%, 75% 0%, 100% 50%, 75% 100%, 0% 100%);
border-radius: 0;
&:hover {
color: var(--md-sys-color-on-tertiary);
background: var(--md-sys-color-tertiary);
&,
~ * {
translate: 8px 0;
}
}
}
}
.actions {
display: flex;
gap: 8px;
align-items: center;
}
.icon.account {
font-size: 32px;
color: var(--md-sys-color-on-secondary-container);
background: var(--md-sys-color-secondary-container);
}
</style>

View File

@@ -0,0 +1,5 @@
<svg>
<defs />
<circle cx="16" cy="16" r="16" color="black" />
</svg>

After

Width:  |  Height:  |  Size: 75 B

View File

@@ -0,0 +1,158 @@
<script>
import {serialLog, serialPort} from "$lib/serial/connection.js"
import {slide} from "svelte/transition"
/**
* @param event {KeyboardEvent}
*/
function keypress(event) {
if (event.code === "Enter") {
event.preventDefault()
const command = prompt.textContent.trim()
prompt.textContent = ""
$serialPort.send(command)
io.scrollTo({top: io.scrollHeight})
}
}
/** @type {HTMLSpanElement} */
let prompt
/** @type {HTMLDivElement} */
let io
</script>
<div class="terminal" tabindex="0" on:focus={() => prompt.focus()}>
<div bind:this={io} class="io">
{#each $serialLog as { type, value }}
<p class={type} transition:slide>{value}</p>
{/each}
<div class="anchor" />
</div>
<div class="prompt">
<b>$</b><span on:keypress={keypress} bind:this={prompt} contenteditable class="prompt" />
<div class="resize-me" />
</div>
</div>
<style lang="scss">
.terminal {
resize: both;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
width: calc(min(100%, 16.5cm));
height: 8cm;
font-family: "Noto Sans Mono", monospace;
font-size: 0.75rem;
color: var(--md-sys-color-on-secondary);
background: var(--md-sys-color-secondary);
border-radius: 16px;
}
::-webkit-scrollbar {
width: 8px;
background: transparent;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--md-sys-color-on-secondary);
border-radius: 4px;
}
::-webkit-resizer {
display: none;
}
.io {
overflow-y: auto;
flex: 1;
padding: 12px;
color: var(--md-sys-color-on-secondary-container);
background: var(--md-sys-color-secondary-container);
border-radius: 0 0 16px 16px;
}
:focus-visible {
outline: none;
}
.prompt {
position: relative;
margin: 0.5rem;
}
.resize-me {
position: absolute;
right: -2px;
bottom: 0;
rotate: -45deg;
width: 10px;
height: 5px;
background: var(--md-sys-color-on-secondary);
clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.anchor {
overflow-anchor: auto;
height: 1px;
}
p {
overflow-anchor: none;
margin-block: 0.15rem;
}
p.input {
margin-block-end: 0.25rem;
font-weight: bold;
}
p.system {
display: flex;
justify-content: center;
margin-block-end: 1rem;
padding: 0.25rem;
color: var(--md-sys-color-on-secondary);
background: var(--md-sys-color-secondary);
border-radius: 8px;
}
p.input::before {
content: "> ";
font-weight: 900;
color: var(--md-sys-color-primary);
}
::selection {
color: var(--md-sys-color-background);
background: var(--md-sys-color-on-background);
}
@keyframes blink {
100%,
60% {
opacity: 1;
}
40%,
0% {
opacity: 0;
}
}
</style>