mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-21 01:12:59 +00:00
initial commit
This commit is contained in:
114
src/lib/components/Navigation.svelte
Normal file
114
src/lib/components/Navigation.svelte
Normal 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>
|
||||
5
src/lib/components/StickCap.svelte
Normal file
5
src/lib/components/StickCap.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg>
|
||||
<defs />
|
||||
|
||||
<circle cx="16" cy="16" r="16" color="black" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 75 B |
158
src/lib/components/Terminal.svelte
Normal file
158
src/lib/components/Terminal.svelte
Normal 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>
|
||||
Reference in New Issue
Block a user