Skip to content

Commit

Permalink
add terminal en nieuwe blokken
Browse files Browse the repository at this point in the history
  • Loading branch information
ThijmenKetelaar committed Feb 27, 2025
1 parent b349d7a commit 46023c2
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 143 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@fortawesome/free-solid-svg-icons": "6.6.0",
"@leaphy-robotics/avrdude-webassembly": "1.7.0",
"@leaphy-robotics/dfu-util-wasm": "^1.0.2",
"@leaphy-robotics/leaphy-blocks": "^3.2.1-python",
"@leaphy-robotics/leaphy-blocks": "3.2.2-python",
"@leaphy-robotics/picotool-wasm": "1.0.3",
"@leaphy-robotics/webusb-ftdi": "1.0.1",
"@sentry/svelte": "8.0.0-beta.5",
Expand Down
49 changes: 39 additions & 10 deletions src/lib/components/core/header/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ async function newProject() {
}
function serialize() {
if ($mode === Mode.BLOCKS)
if ($mode === Mode.BLOCKS) {
return JSON.stringify(serialization.workspaces.save($workspace));
} else if ($mode === Mode.PYTHONBLOCKS) {
// Serialiseer de werkruimte voor de Python-blocks modus
return JSON.stringify(serialization.workspaces.save($workspace)); // Of een andere specifieke functie
}
return $code;
}
Expand All @@ -116,6 +120,7 @@ async function saveProjectAs() {
let extension = $robot.id;
if ($mode === Mode.ADVANCED) extension = "ino";
if ($mode === Mode.PYTHON) extension = "py";
if ($mode === Mode.PYTHONBLOCKS) extension = "py.json";
const url = URL.createObjectURL(
new Blob([serialize()], { type: "text/plain" }),
Expand Down Expand Up @@ -144,14 +149,13 @@ async function openProject() {
mode.set(Mode.PYTHON);
robot.set(robots.l_nano_rp2040);
code.set(await content.text());
} else {
if (get(mode) === Mode.BLOCKS) {
loadWorkspaceFromString(await content.text(), $workspace);
} else {
restore.set(JSON.parse(await content.text()));
mode.set(Mode.BLOCKS);
}
robot.set(robots[file.name.split(".").at(-1)]);
} else if (file.name.endsWith(".py.json")) {
mode.set(Mode.PYTHONBLOCKS);
robot.set(robots.l_nano_rp2040);
loadWorkspaceFromString(await content.text(), $workspace);
} else if (file.name.endsWith(".json")) {
mode.set(Mode.BLOCKS);
loadWorkspaceFromString(await content.text(), $workspace);
}
}
Expand Down Expand Up @@ -290,6 +294,14 @@ function runPython() {
const io = get(microPythonIO);
microPythonRun.set(io.runCode(get(code)));
}
function switchToPythonCode() {
mode.set(Mode.PYTHON);
}
function switchToPythonBlocks() {
mode.set(Mode.PYTHONBLOCKS);
}
</script>

{#snippet projectContext(open: Writable<boolean>)}
Expand Down Expand Up @@ -417,7 +429,7 @@ function runPython() {
</div>

<div class="comp">
{#if $screen === Screen.WORKSPACE && $mode === Mode.BLOCKS}
{#if $screen === Screen.WORKSPACE && ($mode === Mode.BLOCKS || $mode === Mode.PYTHONBLOCKS)}
<Button mode={"outlined"} icon={faUndo} onclick={undo} disabled={!$canUndo} />
<Button mode={"outlined"} icon={faRedo} onclick={redo} disabled={!$canRedo} />
{/if}
Expand All @@ -441,12 +453,29 @@ function runPython() {
/>
{/if}

{#if $mode === Mode.PYTHONBLOCKS}
<Button
mode={"outlined"}
icon={faPen}
name={$_("CODE")}
onclick={switchToPythonCode}
/>
{:else if $mode === Mode.PYTHON}
<Button
mode={"outlined"}
icon={block}
name={$_("BLOCKS")}
onclick={switchToPythonBlocks}
/>
{/if}

<Button
icon={faSave}
name={$_("SAVE")}
mode={"outlined"}
onclick={saveDynamic}
/>

{#if $mode === Mode.PYTHON || $mode === Mode.PYTHONBLOCKS}
{#if $microPythonIO}
<Button
Expand Down
85 changes: 45 additions & 40 deletions src/lib/components/core/popups/popups/PythonUploader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,46 +50,51 @@ async function upload(res: Record<string, string>) {
}
onMount(async () => {
try {
const installed = await io.enterREPLMode();
progress += 100 / 6;
currentState = "CHOOSING_ROBOT";
if (!installed) {
const newRobot = await new Promise<RobotDevice>(
(resolve) => (robotRequest = resolve),
);
robot.set(newRobot);
}
robotRequest = undefined;
progress += 100 / 6;
currentState = "DOWNLOADING_FIRMWARE";
let firmware: Record<string, string>;
if (!installed) firmware = await io.getFirmware(get(robot));
progress += 100 / 6;
currentState = "UPLOADING_FIRMWARE";
if (!installed) await upload(firmware);
progress += 100 / 6;
currentState = "CONNECTING";
if (!installed) await io.enterREPLMode();
progress += 100 / 6;
currentState = "INSTALLING_LIBRARIES";
await io.packageManager.flashLibrary(
"github:leaphy-robotics/leaphy-micropython/package.json",
);
progress += 100 / 6;
popups.close($popupState.id);
} catch (e) {
done = true;
currentState = e?.name || "UPDATE_FAILED";
error = e.description;
throw e;
}
try {
console.log("Entering REPL mode...");
const installed = await io.enterREPLMode();
progress += 100 / 6;
currentState = "CHOOSING_ROBOT";
if (!installed) {
const newRobot = await new Promise<RobotDevice>(
(resolve) => (robotRequest = resolve),
);
robot.set(newRobot);
}
robotRequest = undefined;
progress += 100 / 6;
currentState = "DOWNLOADING_FIRMWARE";
let firmware: Record<string, string>;
if (!installed) {
firmware = await io.getFirmware(get(robot));
console.log("Firmware downloaded:", firmware); // Log de firmware
}
progress += 100 / 6;
currentState = "UPLOADING_FIRMWARE";
if (!installed) await upload(firmware);
progress += 100 / 6;
currentState = "CONNECTING";
if (!installed) await io.enterREPLMode();
progress += 100 / 6;
currentState = "INSTALLING_LIBRARIES";
await io.packageManager.flashLibrary(
"github:leaphy-robotics/leaphy-micropython/package.json",
);
progress += 100 / 6;
popups.close($popupState.id);
} catch (e) {
done = true;
currentState = e?.name || "UPDATE_FAILED";
error = e.description;
console.error("Error during upload:", e); // Log de fout
throw e;
}
});
function close() {
Expand Down
178 changes: 178 additions & 0 deletions src/lib/components/core/popups/popups/TerminalPopup.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<script lang="ts">
import { onMount, onDestroy, createEventDispatcher } from "svelte";
import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { microPythonIO, microPythonRun } from "$state/workspace.svelte";
import { get } from "svelte/store";
// Maak een dispatcher aan om events naar de parent te sturen
const dispatch = createEventDispatcher();
const PROMPT = "$ ";
let terminal: Terminal;
let fitAddon: FitAddon;
let terminalContainer: HTMLDivElement;
let line = "";
let history = [""];
let historyPosition = 0;
let pos = 0;
function renderPrompt() {
if (get(microPythonIO)?.running) return;
terminal.write(`\x1b[2K\r${PROMPT}${line}`);
const virtPos = line.length - pos;
if (virtPos > 0) {
terminal.write(`\x1b[${virtPos}D`);
}
}
onMount(() => {
terminal = new Terminal({
cursorBlink: true,
theme: { background: "#0000", foreground: "#fff" }
});
fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);
terminal.open(terminalContainer);
fitAddon.fit();
terminal.write(PROMPT);
// Abonneer op microPythonIO voor invoerverwerking
const unsubscribeIO = microPythonIO.subscribe((io) => {
if (!io) return;
renderPrompt();
terminal.onData((data: string) => {
if (io.running) return;
switch (data) {
case "\x1b[D": // Pijl-links
pos = Math.max(0, pos - 1);
break;
case "\x1b[C": // Pijl-rechts
pos = Math.min(line.length, pos + 1);
break;
case "\x1b[A": // Omhoog
historyPosition = Math.max(0, historyPosition - 1);
line = history[historyPosition];
pos = line.length;
break;
case "\x1b[B": // Omlaag
historyPosition = Math.min(history.length - 1, historyPosition + 1);
line = history[historyPosition];
pos = line.length;
break;
case "\u007f": // Backspace
if (pos > 0) {
line = line.slice(0, pos - 1) + line.slice(pos);
pos--;
}
break;
case "\u0003": // Ctrl+C
terminal.write("^C\r\n");
line = "";
pos = 0;
break;
case "\r": // Enter
terminal.write("\r\n");
history.push(line);
historyPosition = history.length;
if (line.trim() !== "") {
const events = io.runCode(line);
events.addEventListener("stdout", (event: any) => {
terminal.write(event.data);
});
events.addEventListener("stderr", (event: any) => {
terminal.write(`\x1b[31m${event.data}\x1b[0m`);
});
events.addEventListener("done", renderPrompt);
} else {
renderPrompt();
}
line = "";
pos = 0;
return;
default:
if (
data.length === 1 &&
data.charCodeAt(0) >= 32 &&
data.charCodeAt(0) <= 126
) {
line = line.slice(0, pos) + data + line.slice(pos);
pos++;
}
}
renderPrompt();
});
});
// Abonneer op microPythonRun voor externe output
const unsubscribeRun = microPythonRun.subscribe((events) => {
if (!events) return;
terminal.write("\r\n");
events.addEventListener("stdout", (event: any) => {
terminal.write(event.data);
});
events.addEventListener("stderr", (event: any) => {
terminal.write(`\x1b[31m${event.data}\x1b[0m`);
});
events.addEventListener("done", renderPrompt);
});
onDestroy(() => {
unsubscribeIO();
unsubscribeRun();
terminal.dispose();
});
});
</script>

<div class="popup">
<div class="header">
<h2>Terminal Monitor</h2>
<!-- Klik op de knop dispatcht een "close" event -->
<button on:click={() => dispatch("close")}>Close</button>
</div>
<div class="terminal-container" bind:this={terminalContainer}></div>
</div>

<style>
.popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 800px;
height: 500px;
background: #000;
color: #fff;
display: flex;
flex-direction: column;
z-index: 1000;
border: 1px solid #333;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: #333;
}
.terminal-container {
flex: 1;
}
button {
background: #444;
border: none;
color: #fff;
padding: 5px 10px;
cursor: pointer;
}
button:hover {
background: #555;
}
</style>

Loading

0 comments on commit 46023c2

Please sign in to comment.