mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-02-23 09:32:08 +00:00
feat: improve UF2 flow
This commit is contained in:
@@ -4,6 +4,7 @@ const config = {
|
|||||||
"node_modules/@fontsource-variable/material-symbols-rounded/files/material-symbols-rounded-latin-full-normal.woff2",
|
"node_modules/@fontsource-variable/material-symbols-rounded/files/material-symbols-rounded-latin-full-normal.woff2",
|
||||||
outputPath: "src/lib/assets/icons.min.woff2",
|
outputPath: "src/lib/assets/icons.min.woff2",
|
||||||
icons: [
|
icons: [
|
||||||
|
"deployed_code_update",
|
||||||
"adjust",
|
"adjust",
|
||||||
"add",
|
"add",
|
||||||
"piano",
|
"piano",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"@types/flexsearch": "^0.7.6",
|
"@types/flexsearch": "^0.7.6",
|
||||||
"@types/w3c-web-serial": "^1.0.6",
|
"@types/w3c-web-serial": "^1.0.6",
|
||||||
"@types/w3c-web-usb": "^1.0.10",
|
"@types/w3c-web-usb": "^1.0.10",
|
||||||
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@vite-pwa/sveltekit": "^0.6.0",
|
"@vite-pwa/sveltekit": "^0.6.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
|
|||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -74,6 +74,9 @@ importers:
|
|||||||
'@types/w3c-web-usb':
|
'@types/w3c-web-usb':
|
||||||
specifier: ^1.0.10
|
specifier: ^1.0.10
|
||||||
version: 1.0.10
|
version: 1.0.10
|
||||||
|
'@types/wicg-file-system-access':
|
||||||
|
specifier: ^2023.10.5
|
||||||
|
version: 2023.10.5
|
||||||
'@vite-pwa/sveltekit':
|
'@vite-pwa/sveltekit':
|
||||||
specifier: ^0.6.0
|
specifier: ^0.6.0
|
||||||
version: 0.6.0(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.221)(vite@5.3.5(@types/node@20.14.10)(sass@1.77.8)(terser@5.31.1)))(svelte@5.0.0-next.221)(vite@5.3.5(@types/node@20.14.10)(sass@1.77.8)(terser@5.31.1)))(vite-plugin-pwa@0.20.1(vite@5.3.5(@types/node@20.14.10)(sass@1.77.8)(terser@5.31.1))(workbox-build@7.1.1)(workbox-window@7.1.0))
|
version: 0.6.0(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.221)(vite@5.3.5(@types/node@20.14.10)(sass@1.77.8)(terser@5.31.1)))(svelte@5.0.0-next.221)(vite@5.3.5(@types/node@20.14.10)(sass@1.77.8)(terser@5.31.1)))(vite-plugin-pwa@0.20.1(vite@5.3.5(@types/node@20.14.10)(sass@1.77.8)(terser@5.31.1))(workbox-build@7.1.1)(workbox-window@7.1.0))
|
||||||
@@ -1408,6 +1411,9 @@ packages:
|
|||||||
'@types/w3c-web-usb@1.0.10':
|
'@types/w3c-web-usb@1.0.10':
|
||||||
resolution: {integrity: sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ==}
|
resolution: {integrity: sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ==}
|
||||||
|
|
||||||
|
'@types/wicg-file-system-access@2023.10.5':
|
||||||
|
resolution: {integrity: sha512-e9kZO9kCdLqT2h9Tw38oGv9UNzBBWaR1MzuAavxPcsV/7FJ3tWbU6RI3uB+yKIDPGLkGVbplS52ub0AcRLvrhA==}
|
||||||
|
|
||||||
'@types/yauzl@2.10.3':
|
'@types/yauzl@2.10.3':
|
||||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||||
|
|
||||||
@@ -5537,6 +5543,8 @@ snapshots:
|
|||||||
|
|
||||||
'@types/w3c-web-usb@1.0.10': {}
|
'@types/w3c-web-usb@1.0.10': {}
|
||||||
|
|
||||||
|
'@types/wicg-file-system-access@2023.10.5': {}
|
||||||
|
|
||||||
'@types/yauzl@2.10.3':
|
'@types/yauzl@2.10.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.14.10
|
'@types/node': 20.14.10
|
||||||
|
|||||||
55
src/lib/PageTransition.svelte
Normal file
55
src/lib/PageTransition.svelte
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fly } from "svelte/transition";
|
||||||
|
import { afterNavigate, beforeNavigate } from "$app/navigation";
|
||||||
|
import { expoIn, expoOut } from "svelte/easing";
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
let { children, routeOrder }: { children: Snippet; routeOrder: string[]; direction: } =
|
||||||
|
$props();
|
||||||
|
|
||||||
|
let inDirection = $state(0);
|
||||||
|
let outDirection = $state(0);
|
||||||
|
let outroEnd: undefined | (() => void) = $state(undefined);
|
||||||
|
let animationDone: Promise<void>;
|
||||||
|
|
||||||
|
let isNavigating = $state(false);
|
||||||
|
|
||||||
|
function routeIndex(route: string | undefined): number {
|
||||||
|
return routeOrder.findIndex((it) => route?.startsWith(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeNavigate((navigation) => {
|
||||||
|
const from = routeIndex(navigation.from?.url.pathname);
|
||||||
|
const to = routeIndex(navigation.to?.url.pathname);
|
||||||
|
if (from === -1 || to === -1 || from === to) return;
|
||||||
|
isNavigating = true;
|
||||||
|
|
||||||
|
inDirection = from > to ? -1 : 1;
|
||||||
|
outDirection = from > to ? 1 : -1;
|
||||||
|
|
||||||
|
animationDone = new Promise((resolve) => {
|
||||||
|
outroEnd = resolve;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterNavigate(async () => {
|
||||||
|
await animationDone;
|
||||||
|
isNavigating = false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !isNavigating}
|
||||||
|
<main
|
||||||
|
in:fly={{ y: inDirection * 24, duration: 150, easing: expoOut }}
|
||||||
|
out:fly={{ y: outDirection * 24, duration: 150, easing: expoIn }}
|
||||||
|
onoutroend={outroEnd}
|
||||||
|
>
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
main {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -53,11 +53,13 @@ export interface ProgressInfo {
|
|||||||
}
|
}
|
||||||
export const syncProgress = writable<ProgressInfo | undefined>(undefined);
|
export const syncProgress = writable<ProgressInfo | undefined>(undefined);
|
||||||
|
|
||||||
export async function initSerial(manual = false) {
|
export async function initSerial(manual = false, withSync = true) {
|
||||||
const device = get(serialPort) ?? new CharaDevice();
|
const device = get(serialPort) ?? new CharaDevice();
|
||||||
await device.init(manual);
|
await device.init(manual);
|
||||||
serialPort.set(device);
|
serialPort.set(device);
|
||||||
await sync();
|
if (withSync) {
|
||||||
|
await sync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sync() {
|
export async function sync() {
|
||||||
|
|||||||
@@ -14,27 +14,26 @@
|
|||||||
let isNavigating = $state(false);
|
let isNavigating = $state(false);
|
||||||
|
|
||||||
const routeOrder = [
|
const routeOrder = [
|
||||||
"/config/chords/",
|
"/config",
|
||||||
"/config/layout/",
|
"/learn",
|
||||||
"/config/settings/",
|
"/docs",
|
||||||
|
"/editor",
|
||||||
|
"/chat",
|
||||||
|
"/plugin",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function routeIndex(route: string | undefined): number {
|
||||||
|
return routeOrder.findIndex((it) => route?.startsWith(it));
|
||||||
|
}
|
||||||
|
|
||||||
beforeNavigate((navigation) => {
|
beforeNavigate((navigation) => {
|
||||||
const from = navigation.from?.url.pathname;
|
const from = routeIndex(navigation.from?.url.pathname);
|
||||||
const to = navigation.to?.url.pathname;
|
const to = routeIndex(navigation.to?.url.pathname);
|
||||||
if (from === to) return;
|
if (from === -1 || to === -1 || from === to) return;
|
||||||
isNavigating = true;
|
isNavigating = true;
|
||||||
|
|
||||||
if (!(from && to && routeOrder.includes(from) && routeOrder.includes(to))) {
|
inDirection = from > to ? -1 : 1;
|
||||||
inDirection = 0;
|
outDirection = from > to ? 1 : -1;
|
||||||
outDirection = 0;
|
|
||||||
} else {
|
|
||||||
const fromIndex = routeOrder.indexOf(from);
|
|
||||||
const toIndex = routeOrder.indexOf(to);
|
|
||||||
|
|
||||||
inDirection = fromIndex > toIndex ? -1 : 1;
|
|
||||||
outDirection = fromIndex > toIndex ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
animationDone = new Promise((resolve) => {
|
animationDone = new Promise((resolve) => {
|
||||||
outroEnd = resolve;
|
outroEnd = resolve;
|
||||||
@@ -49,8 +48,8 @@
|
|||||||
|
|
||||||
{#if !isNavigating}
|
{#if !isNavigating}
|
||||||
<main
|
<main
|
||||||
in:fly={{ x: inDirection * 24, duration: 150, easing: expoOut }}
|
in:fly={{ y: inDirection * 24, duration: 150, easing: expoOut }}
|
||||||
out:fly={{ x: outDirection * 24, duration: 150, easing: expoIn }}
|
out:fly={{ y: outDirection * 24, duration: 150, easing: expoIn }}
|
||||||
onoutroend={outroEnd}
|
onoutroend={outroEnd}
|
||||||
>
|
>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
|
import PageTransition from "./PageTransition.svelte";
|
||||||
import Navigation from "./Navigation.svelte";
|
import Navigation from "./Navigation.svelte";
|
||||||
|
|
||||||
let { children }: { children?: Snippet } = $props();
|
let { children }: { children?: Snippet } = $props();
|
||||||
@@ -8,5 +9,9 @@
|
|||||||
<Navigation />
|
<Navigation />
|
||||||
|
|
||||||
{#if children}
|
{#if children}
|
||||||
{@render children()}
|
<PageTransition>
|
||||||
|
{#if children}
|
||||||
|
{@render children()}
|
||||||
|
{/if}
|
||||||
|
</PageTransition>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
65
src/routes/(app)/config/PageTransition.svelte
Normal file
65
src/routes/(app)/config/PageTransition.svelte
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fly } from "svelte/transition";
|
||||||
|
import { afterNavigate, beforeNavigate } from "$app/navigation";
|
||||||
|
import { expoIn, expoOut } from "svelte/easing";
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
let { children }: { children: Snippet } = $props();
|
||||||
|
|
||||||
|
let inDirection = $state(0);
|
||||||
|
let outDirection = $state(0);
|
||||||
|
let outroEnd: undefined | (() => void) = $state(undefined);
|
||||||
|
let animationDone: Promise<void>;
|
||||||
|
|
||||||
|
let isNavigating = $state(false);
|
||||||
|
|
||||||
|
const routeOrder = [
|
||||||
|
"/config/chords/",
|
||||||
|
"/config/layout/",
|
||||||
|
"/config/settings/",
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeNavigate((navigation) => {
|
||||||
|
const from = navigation.from?.url.pathname;
|
||||||
|
const to = navigation.to?.url.pathname;
|
||||||
|
if (from === to) return;
|
||||||
|
isNavigating = true;
|
||||||
|
|
||||||
|
if (!(from && to && routeOrder.includes(from) && routeOrder.includes(to))) {
|
||||||
|
inDirection = 0;
|
||||||
|
outDirection = 0;
|
||||||
|
} else {
|
||||||
|
const fromIndex = routeOrder.indexOf(from);
|
||||||
|
const toIndex = routeOrder.indexOf(to);
|
||||||
|
|
||||||
|
inDirection = fromIndex > toIndex ? -1 : 1;
|
||||||
|
outDirection = fromIndex > toIndex ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
animationDone = new Promise((resolve) => {
|
||||||
|
outroEnd = resolve;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterNavigate(async () => {
|
||||||
|
await animationDone;
|
||||||
|
isNavigating = false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !isNavigating}
|
||||||
|
<main
|
||||||
|
in:fly={{ x: inDirection * 24, duration: 150, easing: expoOut }}
|
||||||
|
out:fly={{ x: outDirection * 24, duration: 150, easing: expoIn }}
|
||||||
|
onoutroend={outroEnd}
|
||||||
|
>
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
main {
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -315,7 +315,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
font-size: 12px;
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
|
|||||||
@@ -4,15 +4,6 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
let files: FileList | null = $state(null);
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
const file = files?.[0];
|
|
||||||
if (file && $serialPort) {
|
|
||||||
$serialPort.updateFirmware(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let currentDevice = $derived(
|
let currentDevice = $derived(
|
||||||
$serialPort
|
$serialPort
|
||||||
? `${$serialPort.device.toLowerCase()}_${$serialPort.chipset.toLowerCase()}`
|
? `${$serialPort.device.toLowerCase()}_${$serialPort.chipset.toLowerCase()}`
|
||||||
@@ -34,8 +25,6 @@
|
|||||||
<aside transition:slide>Connect your device to see which one you need</aside>
|
<aside transition:slide>Connect your device to see which one you need</aside>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<input type="file" accept=".bin" bind:files />
|
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
ul {
|
ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { serialPort } from "$lib/serial/connection";
|
import { downloadBackup } from "$lib/backup/backup";
|
||||||
import { slide } from "svelte/transition";
|
import { initSerial, serialPort } from "$lib/serial/connection";
|
||||||
|
import { fade, slide } from "svelte/transition";
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@
|
|||||||
let success = $state(false);
|
let success = $state(false);
|
||||||
let error = $state<Error | undefined>(undefined);
|
let error = $state<Error | undefined>(undefined);
|
||||||
|
|
||||||
|
let step = $state(0);
|
||||||
|
|
||||||
async function update() {
|
async function update() {
|
||||||
working = true;
|
working = true;
|
||||||
error = undefined;
|
error = undefined;
|
||||||
@@ -59,6 +62,46 @@
|
|||||||
return `${(value / 1024 / 1024).toFixed(2)}MB`;
|
return `${(value / 1024 / 1024).toFixed(2)}MB`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function connect() {
|
||||||
|
try {
|
||||||
|
await initSerial(true, false);
|
||||||
|
step = 1;
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function backup() {
|
||||||
|
downloadBackup();
|
||||||
|
step = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootloader() {
|
||||||
|
$serialPort?.bootloader();
|
||||||
|
$serialPort = undefined;
|
||||||
|
step = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFileSystem() {
|
||||||
|
if (!uf2Url) return;
|
||||||
|
const uf2Promise = fetch(uf2Url).then((it) => it.blob());
|
||||||
|
const handle = await window.showSaveFilePicker({
|
||||||
|
id: `${data.device}-update`,
|
||||||
|
suggestedName: "CURRENT.UF2",
|
||||||
|
excludeAcceptAllOption: true,
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: "UF2 Firmware",
|
||||||
|
accept: { "application/octet-stream": [".UF2"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
const uf2 = await uf2Promise;
|
||||||
|
await uf2.stream().pipeTo(writable);
|
||||||
|
step = 4;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -71,6 +114,44 @@
|
|||||||
to <em class="version">{data.version}</em>
|
to <em class="version">{data.version}</em>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
{#if data.ota && !data.device.endsWith("m0")}
|
||||||
|
{@const buttonError = error || (!success && isCorrectDevice === false)}
|
||||||
|
<section>
|
||||||
|
<button
|
||||||
|
class="update-button"
|
||||||
|
class:working
|
||||||
|
class:primary={!buttonError}
|
||||||
|
class:error={buttonError}
|
||||||
|
disabled={working || $serialPort === undefined || !isCorrectDevice}
|
||||||
|
onclick={update}>Apply Update</button
|
||||||
|
>
|
||||||
|
{#if $serialPort && isCorrectDevice}
|
||||||
|
<div transition:slide>
|
||||||
|
Your device is ready and compatible. Click the button to perform the
|
||||||
|
update.
|
||||||
|
</div>
|
||||||
|
{:else if $serialPort && isCorrectDevice === false}
|
||||||
|
<div class="error" transition:slide>
|
||||||
|
Your device is incompatible with the selected update.
|
||||||
|
</div>
|
||||||
|
{:else if success}
|
||||||
|
<div class="primary" transition:slide>Update successful</div>
|
||||||
|
{:else if error}
|
||||||
|
<div class="error" transition:slide>{error.message}</div>
|
||||||
|
{:else if working}
|
||||||
|
<div class="primary" transition:slide>Updating your device...</div>
|
||||||
|
{:else}
|
||||||
|
<div class="primary" transition:slide>
|
||||||
|
Connect your device to continue
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h3>Manual Update</h3>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<ul class="files">
|
<ul class="files">
|
||||||
{#if data.uf2}
|
{#if data.uf2}
|
||||||
<li>
|
<li>
|
||||||
@@ -99,59 +180,47 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h3>OTA Upate</h3>
|
<h4>UF2 Instructions</h4>
|
||||||
{#if data.ota}
|
|
||||||
{@const buttonError = error || (!success && isCorrectDevice === false)}
|
|
||||||
<button
|
|
||||||
class:working
|
|
||||||
class:primary={!buttonError}
|
|
||||||
class:error={buttonError}
|
|
||||||
disabled={working || $serialPort === undefined || !isCorrectDevice}
|
|
||||||
onclick={update}>Apply Update</button
|
|
||||||
>
|
|
||||||
{#if $serialPort && isCorrectDevice}
|
|
||||||
<div transition:slide>
|
|
||||||
Your device is ready and compatible. Click the button to perform the
|
|
||||||
update.
|
|
||||||
</div>
|
|
||||||
{:else if $serialPort && isCorrectDevice === false}
|
|
||||||
<div class="error" transition:slide>
|
|
||||||
Your device is incompatible with the selected update.
|
|
||||||
</div>
|
|
||||||
{:else if success}
|
|
||||||
<div class="primary" transition:slide>Update successful</div>
|
|
||||||
{:else if error}
|
|
||||||
<div class="error" transition:slide>{error.message}</div>
|
|
||||||
{:else if working}
|
|
||||||
<div class="primary" transition:slide>Updating your device...</div>
|
|
||||||
{:else}
|
|
||||||
<div class="primary" transition:slide>
|
|
||||||
Connect your device to continue
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<em>There are no OTA files for this device.</em>
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<h3>Other options</h3>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h4>Via UF2</h4>
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Backup your device</li>
|
<li>
|
||||||
<li>Reboot to bootloader</li>
|
<button class="inline-button" onclick={connect}
|
||||||
<li>Save CURRENT.UF2 to the new drive</li>
|
><span class="icon">usb</span>Connect</button
|
||||||
<li>Restore</li>
|
>
|
||||||
|
your device
|
||||||
|
{#if step >= 1}
|
||||||
|
<span class="icon ok" transition:fade>check_circle</span>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class:faded={step < 1}>
|
||||||
|
Make a <button class="inline-button" onclick={backup}
|
||||||
|
><span class="icon">download</span>Backup</button
|
||||||
|
>
|
||||||
|
{#if step >= 2}
|
||||||
|
<span class="icon ok" transition:fade>check_circle</span>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class:faded={step < 2}>
|
||||||
|
Reboot to <button class="inline-button" onclick={bootloader}
|
||||||
|
><span class="icon">restart_alt</span>Bootloader</button
|
||||||
|
>
|
||||||
|
{#if step >= 3}
|
||||||
|
<span class="icon ok" transition:fade>check_circle</span>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class:faded={step < 3}>
|
||||||
|
Replace <button class="inline-button" onclick={getFileSystem}
|
||||||
|
><span class="icon">deployed_code_update</span>CURRENT.UF2</button
|
||||||
|
>
|
||||||
|
on the new drive
|
||||||
|
{#if step >= 4}
|
||||||
|
<span class="icon ok" transition:fade>check_circle</span>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
|
||||||
<h4>Via Serial</h4>
|
|
||||||
<p>WIP</p>
|
|
||||||
</section>
|
|
||||||
ading 0 Chordmaps.
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -189,7 +258,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button.inline-button {
|
||||||
|
display: inline;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
height: unset;
|
||||||
|
font-size: inherit;
|
||||||
|
color: var(--md-sys-color-primary);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 1.2em;
|
||||||
|
translate: 0 0.1em;
|
||||||
|
padding-inline-end: 0.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.ok {
|
||||||
|
font-size: 1.2em;
|
||||||
|
translate: 0 0.1em;
|
||||||
|
--icon-fill: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faded {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.update-button {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
@@ -250,26 +344,26 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a[download] {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
border: 1px solid var(--md-sys-color-outline);
|
border: 1px solid var(--md-sys-color-outline);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
.size {
|
.size {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
padding-inline-start: 0.4em;
|
padding-inline-start: 0.4em;
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
grid-row: 1 / span 2;
|
grid-row: 1 / span 2;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user