diff --git a/features.json b/features.json
index 9b18c3739..c81b85e04 100644
--- a/features.json
+++ b/features.json
@@ -5,5 +5,7 @@
"makecode": true,
"liveGraphInputValues": true,
"recordingScrubberValues": true,
- "modelValidation": true
+ "modelValidation": true,
+ "modelSettings": true,
+ "fingerprint": true
}
diff --git a/src/App.svelte b/src/App.svelte
index 0d35998d5..b7b4760d5 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -30,7 +30,6 @@
import Router from './router/Router.svelte';
import { Feature, getFeature } from './lib/FeatureToggles';
import { welcomeLog } from './lib/utils/Logger';
- import { DeviceRequestStates, state } from './lib/stores/Stores';
import MediaQuery from './components/layout/MediaQuery.svelte';
import BottomBarMenuView from './components/layout/BottomBarMenuView.svelte';
import CookieBanner from './components/features/cookie-bannner/CookieBanner.svelte';
@@ -39,11 +38,17 @@
import OverlayView from './components/layout/OverlayView.svelte';
import SideBarMenuView from './components/layout/SideBarMenuView.svelte';
import PageContentView from './components/layout/PageContentView.svelte';
+ import { stores } from './lib/stores/Stores';
+ import { DeviceRequestStates } from './lib/domain/Devices';
+ import { isLoading } from './lib/stores/ApplicationState';
+
+ const devices = stores.getDevices();
+
welcomeLog();
if (CookieManager.isReconnectFlagSet()) {
- $state.offerReconnect = true;
- $state.reconnectState = DeviceRequestStates.INPUT;
+ $devices.offerReconnect = true;
+ $devices.reconnectState = DeviceRequestStates.INPUT;
CookieManager.unsetReconnectFlag();
}
@@ -56,7 +61,7 @@
{:else}
- {#if $state.isLoading}
+ {#if $isLoading}
diff --git a/src/StaticConfiguration.ts b/src/StaticConfiguration.ts
index 5aa3aa920..cb9bf868b 100644
--- a/src/StaticConfiguration.ts
+++ b/src/StaticConfiguration.ts
@@ -148,5 +148,10 @@ class StaticConfiguration {
* Whether the knn model should be normalized by default
*/
public static readonly knnNormalizedDefault = false;
+
+ /**
+ * Whether fingerprinting should be enabled by default
+ */
+ public static readonly enableFingerprintByDefault: boolean = false;
}
export default StaticConfiguration;
diff --git a/src/__tests__/csv/csv.test.ts b/src/__tests__/csv/csv.test.ts
new file mode 100644
index 000000000..d864626eb
--- /dev/null
+++ b/src/__tests__/csv/csv.test.ts
@@ -0,0 +1,116 @@
+/**
+ * @vitest-environment jsdom
+ */
+/**
+ * (c) 2023-2025, Center for Computational Thinking and Design at Aarhus University and contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+import { writable } from 'svelte/store';
+import type { RecordingData } from '../../lib/domain/RecordingData';
+import Gesture from '../../lib/domain/stores/gesture/Gesture';
+import type { PersistedGestureData } from '../../lib/domain/stores/gesture/Gestures';
+import type GestureConfidence from '../../lib/domain/stores/gesture/GestureConfidence';
+import {
+ serializeGestureRecordingsToCSV,
+ serializeRecordingToCsvWithoutGestureName,
+} from '../../lib/utils/CSVUtils';
+
+describe('CSV Test', () => {
+ // A crude way to enforce direction of dependencies, inspired by ArchUnit for java
+ test('Convert recording', () => {
+ const input: RecordingData = {
+ ID: 123,
+ labels: ['x', 'y', 'z'],
+ samples: [
+ {
+ vector: [1, 2, 3],
+ },
+ {
+ vector: [4, 5, 6],
+ },
+ {
+ vector: [7, 8, 9],
+ },
+ ],
+ };
+ const data = writable({
+ recordings: [input],
+ name: 'Test;Gesture',
+ } as PersistedGestureData);
+ const confidence = writable({}) as unknown as GestureConfidence;
+ const gesture: Gesture = new Gesture(data, confidence, () => void 0);
+ const result = serializeGestureRecordingsToCSV([gesture]);
+ expect(result).toBe(
+ 'gesture;sample;x;y;z\nTest\\;Gesture;0;1;2;3\nTest\\;Gesture;1;4;5;6\nTest\\;Gesture;2;7;8;9',
+ );
+ });
+
+ test('Convert multiple gestures', () => {
+ const input1: RecordingData = {
+ ID: 123,
+ labels: ['x', 'y', 'z'],
+ samples: [
+ {
+ vector: [1, 2, 3],
+ },
+ {
+ vector: [4, 5, 6],
+ },
+ ],
+ };
+ const input2: RecordingData = {
+ ID: 456,
+ labels: ['x', 'y', 'z'],
+ samples: [
+ {
+ vector: [7, 8, 9],
+ },
+ {
+ vector: [10, 11, 12],
+ },
+ ],
+ };
+ const data1 = writable({
+ recordings: [input1],
+ name: 'Gesture1',
+ } as PersistedGestureData);
+ const data2 = writable({
+ recordings: [input2],
+ name: 'Gesture2',
+ } as PersistedGestureData);
+ const confidence = writable({}) as unknown as GestureConfidence;
+ const gesture1: Gesture = new Gesture(data1, confidence, () => void 0);
+ const gesture2: Gesture = new Gesture(data2, confidence, () => void 0);
+ const result = serializeGestureRecordingsToCSV([gesture1, gesture2]);
+ expect(result).toBe(
+ 'gesture;sample;x;y;z\n' +
+ 'Gesture1;0;1;2;3\n' +
+ 'Gesture1;1;4;5;6\n' +
+ 'Gesture2;0;7;8;9\n' +
+ 'Gesture2;1;10;11;12',
+ );
+ });
+
+ test('Serialize recording without gesture name (with headers)', () => {
+ const input: RecordingData = {
+ ID: 123,
+ labels: ['x', 'y', 'z'],
+ samples: [
+ {
+ vector: [1, 2, 3],
+ },
+ {
+ vector: [4, 5, 6],
+ },
+ {
+ vector: [7, 8, 9],
+ },
+ ],
+ };
+
+ const result = serializeRecordingToCsvWithoutGestureName(input);
+ expect(result).toBe('sample;x;y;z\n' + '0;1;2;3\n' + '1;4;5;6\n' + '2;7;8;9');
+ });
+});
diff --git a/src/__tests__/mocks/mlmodel/TestMLModelTrainer.ts b/src/__tests__/mocks/mlmodel/TestMLModelTrainer.ts
index b32b8e397..faffbf0a0 100644
--- a/src/__tests__/mocks/mlmodel/TestMLModelTrainer.ts
+++ b/src/__tests__/mocks/mlmodel/TestMLModelTrainer.ts
@@ -3,12 +3,17 @@
*
* SPDX-License-Identifier: MIT
*/
+import type { ModelInfo } from '../../../lib/domain/ModelRegistry';
+import ModelRegistry from '../../../lib/domain/ModelRegistry';
import type { ModelTrainer } from '../../../lib/domain/ModelTrainer';
import type { TrainingDataRepository } from '../../../lib/domain/TrainingDataRepository';
import TestMLModel from './TestMLModel';
class TestMLModelTrainer implements ModelTrainer {
constructor(private numberOfGestures: number) {}
+ getModelInfo(): ModelInfo {
+ return ModelRegistry.NeuralNetwork;
+ }
public trainModel(trainingData: TrainingDataRepository): Promise {
return Promise.resolve(new TestMLModel(this.numberOfGestures));
}
diff --git a/src/__viteBuildVariants__/ml-machine-simple/features.json b/src/__viteBuildVariants__/ml-machine-simple/features.json
index 973184add..4ff547fdf 100644
--- a/src/__viteBuildVariants__/ml-machine-simple/features.json
+++ b/src/__viteBuildVariants__/ml-machine-simple/features.json
@@ -5,5 +5,7 @@
"makecode": false,
"liveGraphInputValues": false,
"recordingScrubberValues": false,
- "modelValidation": false
+ "modelValidation": false,
+ "modelSettings": false,
+ "fingerprint": false
}
diff --git a/src/__viteBuildVariants__/ml-machine/features.json b/src/__viteBuildVariants__/ml-machine/features.json
index d8623ddc3..a9bd93e99 100644
--- a/src/__viteBuildVariants__/ml-machine/features.json
+++ b/src/__viteBuildVariants__/ml-machine/features.json
@@ -5,5 +5,7 @@
"makecode": true,
"liveGraphInputValues": true,
"recordingScrubberValues": true,
- "modelValidation": true
+ "modelValidation": true,
+ "modelSettings": true,
+ "fingerprint": true
}
diff --git a/src/__viteBuildVariants__/unbranded/features.json b/src/__viteBuildVariants__/unbranded/features.json
index 9b18c3739..c81b85e04 100644
--- a/src/__viteBuildVariants__/unbranded/features.json
+++ b/src/__viteBuildVariants__/unbranded/features.json
@@ -5,5 +5,7 @@
"makecode": true,
"liveGraphInputValues": true,
"recordingScrubberValues": true,
- "modelValidation": true
+ "modelValidation": true,
+ "modelSettings": true,
+ "fingerprint": true
}
diff --git a/src/assets/messages/ui.da.json b/src/assets/messages/ui.da.json
index cf724cbc2..8ff2b6301 100644
--- a/src/assets/messages/ui.da.json
+++ b/src/assets/messages/ui.da.json
@@ -50,6 +50,7 @@
"content.data.noData.exampleName.shake": "Ryste",
"content.data.noData.exampleName.still": "Stille",
"content.data.noData.exampleName.circle": "Cirkel",
+ "content.data.tooltip.remove": "Fjern",
"content.trainer.failure.header": "Træning mislykkedes",
"content.trainer.failure.body": "Træningen resulterede ikke i en brugbar model. Grunden til dette ligger sandsynligvis i dataet. Hvis dataet i forskellige klasser minder for meget om hinanden, kan dette resultere i nogle forskellige problemer i træningsprocessen, der ikke gør det muligt at træne modellen ordentligt.",
"content.trainer.failure.todo": "Gå tilbage til datasiden og ændr i din data.",
@@ -111,8 +112,9 @@
"content.validation.infobox.classesContent": "Her kan du se de klasser, du har defineret på datasiden.",
"content.validation.noGestures.title": "Ingen klasser...",
"content.validation.noGestures.description": "Du har endnu ikke tilføjet nogen klasser. Gå til datatrinnet i menuen til venstre og tilføj nogle.",
- "content.validation.tutorial.title": "Validierungssätze",
- "content.validation.tutorial.description": "Erstelle einen separaten Datensatz, um die Leistung deines Modells zu bewerten. Das Modell wird nicht mit diesen Daten trainiert.",
+ "content.validation.tutorial.title": "Valideringssæt",
+ "content.validation.tutorial.description": "Opret et separat datasæt til at evaluere dit models ydeevne. Modellen vil ikke blive trænet på disse data.",
+ "content.validation.tutorial.trainmodelfirst": "Zuerst ein Modell trainieren",
"footer.connectButtonNotConnected": "Tilslut din BBC micro:bit",
"footer.disconnectButton": "Frakobl",
"footer.helpHeader": "Live graf",
diff --git a/src/assets/messages/ui.de.json b/src/assets/messages/ui.de.json
index f5fb6bd88..37f092b7a 100644
--- a/src/assets/messages/ui.de.json
+++ b/src/assets/messages/ui.de.json
@@ -50,6 +50,7 @@
"content.data.noData.exampleName.shake": "Schütteln",
"content.data.noData.exampleName.still": "Still",
"content.data.noData.exampleName.circle": "Kreis",
+ "content.data.tooltip.remove": "Entfernen",
"content.trainer.failure.header": "Training fehlgeschlagen",
"content.trainer.failure.body": "Das Training führte nicht zu einem brauchbaren Modell. Der Grund dafür sind höchstwahrscheinlich die für das Training verwendeten Daten. Wenn die Daten für verschiedene Klassen zu ähnlich sind, kann dies zu Problemen im Trainingsprozess führen.",
"content.trainer.failure.todo": "Gehe zur Datenseite zurück und ändere die Daten.",
@@ -111,8 +112,9 @@
"content.validation.infobox.classesContent": "Hier siehst du die Klassen, die du auf der Datenseite definiert hast.",
"content.validation.noGestures.title": "Keine Gesten...",
"content.validation.noGestures.description": "Du hast noch keine Gesten hinzugefügt. Gehe zum Schritt „Daten“ im Menü auf der linken Seite und füge welche hinzu.",
- "content.validation.tutorial.title": "Valideringssæt",
- "content.validation.tutorial.description": "Opret et separat datasæt til at evaluere dit models ydeevne. Modellen vil ikke blive trænet på disse data.",
+ "content.validation.tutorial.title": "Validierungssätze",
+ "content.validation.tutorial.description": "Erstelle einen separaten Datensatz, um die Leistung deines Modells zu bewerten. Das Modell wird nicht mit diesen Daten trainiert.",
+ "content.validation.tutorial.trainmodelfirst": "Zuerst ein Modell trainieren",
"footer.connectButtonNotConnected": "Verbinde deinen micro:bit",
"footer.disconnectButton": "Trennen",
"footer.helpHeader": "Live-Diagramm",
diff --git a/src/assets/messages/ui.en.json b/src/assets/messages/ui.en.json
index 7f15fe567..ea0ec33ab 100644
--- a/src/assets/messages/ui.en.json
+++ b/src/assets/messages/ui.en.json
@@ -50,6 +50,7 @@
"content.data.noData.exampleName.shake": "Shake",
"content.data.noData.exampleName.still": "Still",
"content.data.noData.exampleName.circle": "Circle",
+ "content.data.tooltip.remove": "Remove",
"content.trainer.failure.header": "Training Failed",
"content.trainer.failure.body": "The training did not result in a usable model. The reason for this is most likely the data used for training. If the data for different classes are too similar, this can result in issues in the training process.",
"content.trainer.failure.todo": "Return to the data page and change your data.",
@@ -113,6 +114,7 @@
"content.validation.noGestures.description": "You haven't added any gestures yet. Go to the data step in the menu to the left and add some.",
"content.validation.tutorial.title": "Validation sets",
"content.validation.tutorial.description": "Create a separate dataset to evaluate the performance of your model. The model will not be trained on this data.",
+ "content.validation.tutorial.trainmodelfirst": "Train a model first",
"footer.connectButtonNotConnected": "Connect your micro:bit",
"footer.disconnectButton": "Disconnect",
"footer.helpHeader": "Live graph",
diff --git a/src/components/features/OutdatedMicrobitWarning.svelte b/src/components/features/OutdatedMicrobitWarning.svelte
index f4a20270d..2f18e41d9 100644
--- a/src/components/features/OutdatedMicrobitWarning.svelte
+++ b/src/components/features/OutdatedMicrobitWarning.svelte
@@ -14,8 +14,8 @@
import StaticConfiguration from '../../StaticConfiguration';
import Microbits from '../../lib/microbit-interfacing/Microbits';
import { HexOrigin } from '../../lib/microbit-interfacing/HexOrigin';
- import { DeviceRequestStates } from '../../lib/stores/Stores';
import StandardButton from '../ui/buttons/StandardButton.svelte';
+ import { DeviceRequestStates } from '../../lib/domain/Devices';
let hasBeenClosed = false;
export let targetRole: 'INPUT' | 'OUTPUT';
let showMakeCodeUpdateMessage =
diff --git a/src/components/features/PleaseConnect.svelte b/src/components/features/PleaseConnect.svelte
index eebf28970..efd838f37 100644
--- a/src/components/features/PleaseConnect.svelte
+++ b/src/components/features/PleaseConnect.svelte
@@ -16,10 +16,10 @@
-
+
{$t('menu.trainer.notConnected1')}
-
+
{$t('menu.trainer.notConnected2')}
diff --git a/src/components/features/ReconnectPrompt.svelte b/src/components/features/ReconnectPrompt.svelte
index 8d3681d78..844d2025f 100644
--- a/src/components/features/ReconnectPrompt.svelte
+++ b/src/components/features/ReconnectPrompt.svelte
@@ -10,12 +10,15 @@
import { btPatternInput, btPatternOutput } from '../../lib/stores/connectionStore';
import Microbits from '../../lib/microbit-interfacing/Microbits';
import { MBSpecs } from 'microbyte';
- import { DeviceRequestStates, state } from '../../lib/stores/Stores';
import StandardButton from '../ui/buttons/StandardButton.svelte';
+ import { stores } from '../../lib/stores/Stores';
+ import { DeviceRequestStates } from '../../lib/domain/Devices';
+
+ const devices = stores.getDevices();
let reconnectText: string;
let reconnectButtonText: string;
- state.subscribe(s => {
+ devices.subscribe(s => {
if (s.reconnectState === DeviceRequestStates.INPUT) {
reconnectText = $t('popup.disconnectedWarning.input');
reconnectButtonText = $t('popup.disconnectedWarning.reconnectButton.input');
@@ -26,7 +29,7 @@
});
// When disconnected by lost connection, offer the option to attempt to reconnect
let hideReconnectMessageAfterTimeout = false;
- state.subscribe(s => {
+ devices.subscribe(s => {
if (s.offerReconnect) {
hideReconnectMessageAfterTimeout = true;
}
@@ -47,7 +50,7 @@
};
void connect().then(() => {
- $state.offerReconnect = false;
+ $devices.offerReconnect = false;
});
};
@@ -59,7 +62,7 @@
{$t('popup.connectMB.bluetooth.cancelledConnection.noNameDescription')}
diff --git a/src/components/features/datacollection/Gesture.svelte b/src/components/features/datacollection/Gesture.svelte
index 550c11a01..dc11668e2 100644
--- a/src/components/features/datacollection/Gesture.svelte
+++ b/src/components/features/datacollection/Gesture.svelte
@@ -14,13 +14,13 @@
MicrobitInteractions,
chosenGesture,
} from '../../../lib/stores/uiStore';
- import Recording from '../../ui/Recording.svelte';
+ import Recording from '../../ui/recording/Recording.svelte';
import { t } from '../../../i18n';
import ImageSkeleton from '../../ui/skeletonloading/ImageSkeleton.svelte';
import GestureCard from '../../ui/Card.svelte';
import StaticConfiguration from '../../../StaticConfiguration';
import Gesture from '../../../lib/domain/stores/gesture/Gesture';
- import { state, stores } from '../../../lib/stores/Stores';
+ import { stores } from '../../../lib/stores/Stores';
import type { RecordingData } from '../../../lib/domain/RecordingData';
import { startRecording } from '../../../lib/utils/Recording';
import GestureDot from '../../ui/GestureDot.svelte';
@@ -28,11 +28,12 @@
export let onNoMicrobitSelect: () => void;
export let gesture: Gesture;
+ const devices = stores.getDevices();
const gestures = stores.getGestures();
- $: liveData = $stores.liveData;
const defaultNewName = $t('content.data.classPlaceholderNewClass');
const recordingDuration = StaticConfiguration.recordingDuration;
+ const enableFingerprint = stores.getEnableFingerprint();
let isThisRecording = false;
@@ -86,7 +87,7 @@
// If gesture is already selected, the selection is removed.
// If bluetooth is not connected, open connection prompt by calling callback
function selectClicked(): void {
- if (!$state.isInputConnected) {
+ if (!$devices.isInputConnected) {
chosenGesture.update(gesture => {
gesture = null;
return gesture;
@@ -218,7 +219,12 @@
{#each $gesture.recordings as recording (String($gesture.ID) + String(recording.ID))}
-
+
{/each}
diff --git a/src/components/features/graphs/LiveGraph.svelte b/src/components/features/graphs/LiveGraph.svelte
index b18b379ab..c1928e386 100644
--- a/src/components/features/graphs/LiveGraph.svelte
+++ b/src/components/features/graphs/LiveGraph.svelte
@@ -9,11 +9,11 @@
import { type Unsubscriber } from 'svelte/store';
import { SmoothieChart, TimeSeries } from 'smoothie';
import DimensionLabels from './DimensionLabels.svelte';
- import { state, stores } from '../../../lib/stores/Stores';
import type { LiveData } from '../../../lib/domain/stores/LiveData';
import type { LiveDataVector } from '../../../lib/domain/stores/LiveDataVector';
import StaticConfiguration from '../../../StaticConfiguration';
import SmoothedLiveData from '../../../lib/livedata/SmoothedLiveData';
+ import { stores } from '../../../lib/stores/Stores';
/**
* TimesSeries, but with the data array added.
@@ -32,6 +32,7 @@
let axisColors = StaticConfiguration.graphColors;
const highlightedAxes = stores.getHighlightedAxes();
+ const devices = stores.getDevices();
// Smoothes real-time data by using the 3 most recent data points
let smoothedLiveData = new SmoothedLiveData(liveData, 3);
@@ -98,7 +99,7 @@
const model = classifier.getModel();
$: {
if (chart !== undefined) {
- if ($state.isInputReady) {
+ if ($devices.isInputReady) {
if (!$model.isTraining) {
chart.start();
} else {
@@ -114,7 +115,7 @@
// The jagged edges problem is caused by repeating the recordingStarted function.
// We will simply block the recording from starting, while it's recording
let blockRecordingStart = false;
- $: recordingStarted($state.isRecording);
+ $: recordingStarted($devices.isRecording);
// Function to clearly diplay the area in which users are recording
function recordingStarted(isRecording: boolean): void {
@@ -135,15 +136,15 @@
}, StaticConfiguration.recordingDuration);
}
- // When state changes, update the state of the canvas
+ // When devices changes, update the devices of the canvas
$: {
- const isConnected = $state.isInputReady;
+ const isConnected = $devices.isInputReady;
updateCanvas(isConnected);
}
let unsubscribeFromData: Unsubscriber | undefined;
- // If state is connected. Start updating the graph whenever there is new data
+ // If devices is connected. Start updating the graph whenever there is new data
// From the Micro:Bit
function updateCanvas(isConnected: boolean) {
if (isConnected || !unsubscribeFromData) {
@@ -180,7 +181,7 @@
{#key cnt}
s).subscribe(s => {
init();
});
@@ -97,7 +99,7 @@
{val.toFixed(2)}
diff --git a/src/components/features/graphs/knngraph/KNNModelGraphController.ts b/src/components/features/graphs/knngraph/KNNModelGraphController.ts
index d22373161..9c2b2fdfc 100644
--- a/src/components/features/graphs/knngraph/KNNModelGraphController.ts
+++ b/src/components/features/graphs/knngraph/KNNModelGraphController.ts
@@ -5,10 +5,7 @@
*/
import { type Writable, derived, get, writable } from 'svelte/store';
import KNNModelGraphDrawer, { type GraphDrawConfig } from './KNNModelGraphDrawer';
-import {
- knnCurrentPoint,
- knnTrainingDataPoints,
-} from '../../../../lib/stores/KnnModelGraph';
+import { knnCurrentPoint, knnTrainingDataPoints } from '../../../../lib/stores/KNNStores';
import type Filters from '../../../../lib/domain/Filters';
import { stores } from '../../../../lib/stores/Stores';
import type { Point3D } from '../../../../lib/utils/graphUtils';
diff --git a/src/components/features/graphs/knngraph/KNNModelGraphDrawer.ts b/src/components/features/graphs/knngraph/KNNModelGraphDrawer.ts
index c1d7fd5c0..4acebf7b3 100644
--- a/src/components/features/graphs/knngraph/KNNModelGraphDrawer.ts
+++ b/src/components/features/graphs/knngraph/KNNModelGraphDrawer.ts
@@ -7,9 +7,9 @@ import { gridPlanes3D, points3D, lines3D } from 'd3-3d';
import { knnHighlightedPoint } from './KnnPointToolTip';
import { get } from 'svelte/store';
import * as d3 from 'd3';
-import { knnNeighbours } from '../../../../lib/stores/KnnModelGraph';
+import { knnNeighbours } from '../../../../lib/stores/KNNStores';
import type { Point3D, Point3DTransformed } from '../../../../lib/utils/graphUtils';
-import { state, stores } from '../../../../lib/stores/Stores';
+import { stores } from '../../../../lib/stores/Stores';
import StaticConfiguration from '../../../../StaticConfiguration';
export type GraphDrawConfig = {
@@ -66,7 +66,7 @@ class KNNModelGraphDrawer {
}),
);
- if (get(state).isInputReady) {
+ if (get(stores.getDevices()).isInputReady) {
this.addPoint(drawableLivePoint, 'live');
// Draw lines from live point to the nearest neighbours
diff --git a/src/components/features/model/ModelGestureStack.svelte b/src/components/features/model/ModelGestureStack.svelte
index 9d092ef9e..550b04b65 100644
--- a/src/components/features/model/ModelGestureStack.svelte
+++ b/src/components/features/model/ModelGestureStack.svelte
@@ -19,7 +19,7 @@
import type { SoundData } from '../../../lib/domain/stores/gesture/Gesture';
import type Gesture from '../../../lib/domain/stores/gesture/Gesture';
import Microbits from '../../../lib/microbit-interfacing/Microbits';
- import { state, stores } from '../../../lib/stores/Stores';
+ import { stores } from '../../../lib/stores/Stores';
import StaticConfiguration from '../../../StaticConfiguration';
import Card from '../../ui/Card.svelte';
import GestureDot from '../../ui/GestureDot.svelte';
@@ -33,6 +33,7 @@
import { PinTurnOnState } from '../../../lib/PinTurnOnState';
import { MBSpecs } from 'microbyte';
+ const devices = stores.getDevices();
const gestures = stores.getGestures();
type TriggerAction = 'turnOn' | 'turnOff' | 'none';
@@ -261,7 +262,7 @@
width="30px" />
diff --git a/src/components/features/playground/LiveDataBufferUtilizationPercentage.svelte b/src/components/features/playground/LiveDataBufferUtilizationPercentage.svelte
deleted file mode 100644
index b6013006c..000000000
--- a/src/components/features/playground/LiveDataBufferUtilizationPercentage.svelte
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
Buffer utilization = {utilization * 100}%
-
- Buffer utilization should be about 20-50%. Too high means performance spikes may cause
- the buffer to lack sufficient data to classify. Too low means performance is wasted
-
+ highlightedAxis.set([$availableAxes[0]])}>
+ X
+
+ highlightedAxis.set([$availableAxes[1]])}>
+ Y
+
+ highlightedAxis.set([$availableAxes[2]])}>
+ Z
+
- {/if}
-
-{:else}
-
-
{$t('content.trainer.knn.selectOneAxis')}
-
- highlightedAxis.set([$availableAxes[0]])}>
- X
-
- highlightedAxis.set([$availableAxes[1]])}>
- Y
-
- highlightedAxis.set([$availableAxes[2]])}>
- Z
-