Browse Source

Svelte: Extract a shared component in preparation for supporting multiple images

Alessandro Pignotti 9 months ago
parent
commit
e2589d8179
2 changed files with 182 additions and 175 deletions
  1. 179 0
      src/lib/WebVM.svelte
  2. 3 175
      src/routes/+page.svelte

+ 179 - 0
src/lib/WebVM.svelte

@@ -0,0 +1,179 @@
+<script>
+	import { onMount } from 'svelte';
+	import { Terminal } from '@xterm/xterm';
+	import { FitAddon } from '@xterm/addon-fit';
+	import { WebLinksAddon } from '@xterm/addon-web-links';
+	import Nav from 'labs/packages/global-navbar/src/Nav.svelte';
+	import SideBar from '$lib/SideBar.svelte';
+	import * as CheerpX from '@leaningtech/cheerpx';
+	import '$lib/global.css';
+	import '@xterm/xterm/css/xterm.css'
+	import '@fortawesome/fontawesome-free/css/all.min.css'
+	import { networkInterface, startLogin } from '$lib/network.js'
+	import { cpuActivity, diskActivity } from '$lib/activities.js'
+	import { introMessage, errorMessage } from '$lib/messages.js'
+
+	export let diskImageUrl = null;
+	export let diskImageType = null;
+
+	var term = new Terminal({cursorBlink:true, convertEol:true, fontFamily:"monospace", fontWeight: 400, fontWeightBold: 700});
+	var cx = null;
+	var fitAddon = new FitAddon();
+	term.loadAddon(fitAddon);
+	var linkAddon = new WebLinksAddon();
+	term.loadAddon(linkAddon);
+	var cxReadFunc = null;
+	function writeData(buf)
+	{
+		term.write(new Uint8Array(buf));
+	}
+	function readData(str)
+	{
+		if(cxReadFunc == null)
+			return;
+		for(var i=0;i<str.length;i++)
+			cxReadFunc(str.charCodeAt(i));
+	}
+	function printMessage(msg)
+	{
+		for(var i=0;i<msg.length;i++)
+			term.write(msg[i] + "\n");
+	}
+	function hddCallback(state)
+	{
+		diskActivity.set(state != "ready");
+	}
+	function cpuCallback(state)
+	{
+		cpuActivity.set(state != "ready");
+	}
+	term.onData(readData);
+	function initTerminal()
+	{
+		const consoleDiv = document.getElementById("console");
+		term.open(consoleDiv);
+		term.scrollToTop();
+		fitAddon.fit();
+		window.addEventListener("resize", function(ev){ fitAddon.fit(); });
+		term.focus();
+		// Avoid undesired default DnD handling
+		function preventDefaults (e) {
+			e.preventDefault()
+			e.stopPropagation()
+		}
+		consoleDiv.addEventListener("dragover", preventDefaults, false);
+		consoleDiv.addEventListener("dragenter", preventDefaults, false);
+		consoleDiv.addEventListener("dragleave", preventDefaults, false);
+		consoleDiv.addEventListener("drop", preventDefaults, false);
+		printMessage(introMessage);
+		initCheerpX();
+	}
+	async function initCheerpX()
+	{
+		// TODO: Check for SAB support
+		var blockDevice = null;
+		switch(diskImageType)
+		{
+			case "cloud":
+				try
+				{
+					blockDevice = await CheerpX.CloudDevice.create(diskImageUrl);
+				}
+				catch(e)
+				{
+					// Report the failure and try again with plain HTTP
+					var wssProtocol = "wss:";
+					if(diskImageUrl.startsWith(wssProtocol))
+					{
+						// WebSocket protocol failed, try agin using plain HTTP
+						plausible("WS Disk failure");
+						blockDevice = await CheerpX.CloudDevice.create("https:" + diskImageUrl.substr(wssProtocol.length));
+					}
+					else
+					{
+						// No other recovery option
+						throw e;
+					}
+				}
+				break;
+			case "bytes":
+				blockDevice = await CheerpX.HttpBytesDevice.create(diskImageUrl);
+				break;
+			case "github":
+				blockDevice = await CheerpX.GitHubDevice.create(diskImageUrl);
+				break;
+			default:
+				throw new Error("Unrecognized device type");
+		}
+		var overlayDevice = await CheerpX.OverlayDevice.create(blockDevice, await CheerpX.IDBDevice.create("block1"));
+		var webDevice = await CheerpX.WebDevice.create("");
+		var dataDevice = await CheerpX.DataDevice.create();
+		var mountPoints = [
+			// The root filesystem, as an Ext2 image
+			{type:"ext2", dev:overlayDevice, path:"/"},
+			// Access to files on the Web server, relative to the current page
+			{type:"dir", dev:webDevice, path:"/web"},
+			// Access to read-only data coming from JavaScript
+			{type:"dir", dev:dataDevice, path:"/data"},
+			// Automatically created device files
+			{type:"devs", path:"/dev"},
+			// The Linux 'proc' filesystem which provides information about running processes
+			{type:"proc", path:"/proc"}
+		];
+		try
+		{
+			cx = await CheerpX.Linux.create({mounts: mountPoints, networkInterface: networkInterface});
+		}
+		catch(e)
+		{
+			printMessage(errorMessage);
+			printMessage([e.toString()]);
+			return;
+		}
+		cx.registerCallback("cpuActivity", cpuCallback);
+		cx.registerCallback("diskActivity", hddCallback);
+		term.scrollToBottom();
+		cxReadFunc = cx.setCustomConsole(writeData, term.cols, term.rows);
+		// Reasonable defaults for local deployments
+		// var cmd = "/bin/bash";
+		// var args = ["--login"];
+		// var env = ["HOME=/home/user", "TERM=xterm", "USER=user", "SHELL=/bin/bash", "EDITOR=vim", "LANG=en_US.UTF-8", "LC_ALL=C"];
+		// var cwd = "/home/user";
+		// Executable full path (Required)
+		var cmd = "/bin/bash";
+		// Arguments, as an array (Required)
+		var args = ["--login"]
+		// Optional extra parameters
+		var opts = {
+			// Environment variables
+			env: ["HOME=/home/user", "TERM=xterm", "USER=user", "SHELL=/bin/bash", "EDITOR=vim", "LANG=en_US.UTF-8", "LC_ALL=C"],
+			// Current working directory
+			cwd: "/home/user",
+			// User id
+			uid: 1000,
+			// Group id
+			gid: 1000
+		};
+		// Run the command in a loop, in case the user exits
+		while (true)
+		{
+			await cx.run(cmd, args, opts);
+		}
+	}
+	onMount(initTerminal);
+	async function handleConnect()
+	{
+		const w = window.open("login.html", "_blank");
+		await cx.networkLogin();
+		w.location.href = await startLogin();
+	}
+</script>
+
+<main class="relative w-full h-full">
+	<Nav />
+	<div class="absolute top-10 bottom-0 left-0 right-0">
+		<SideBar on:connect={handleConnect}/>
+		<div class="absolute top-0 bottom-0 left-14 right-0 p-1 scrollbar" id="console">
+		</div>
+	</div>
+</main>

+ 3 - 175
src/routes/+page.svelte

@@ -1,178 +1,6 @@
 <script>
-	import { onMount } from 'svelte';
-	import { Terminal } from '@xterm/xterm';
-	import { FitAddon } from '@xterm/addon-fit';
-	import { WebLinksAddon } from '@xterm/addon-web-links';
-	import Nav from 'labs/packages/global-navbar/src/Nav.svelte';
-	import SideBar from '$lib/SideBar.svelte';
-	import * as CheerpX from '@leaningtech/cheerpx';
-	import { diskImageUrl, diskImageType } from 'diskConfig'
-	import '$lib/global.css';
-	import '@xterm/xterm/css/xterm.css'
-	import '@fortawesome/fontawesome-free/css/all.min.css'
-	import { networkInterface, startLogin } from '$lib/network.js'
-	import { cpuActivity, diskActivity } from '$lib/activities.js'
-	import { introMessage, errorMessage } from '$lib/messages.js'
-
-	var term = new Terminal({cursorBlink:true, convertEol:true, fontFamily:"monospace", fontWeight: 400, fontWeightBold: 700});
-	var cx = null;
-	var fitAddon = new FitAddon();
-	term.loadAddon(fitAddon);
-	var linkAddon = new WebLinksAddon();
-	term.loadAddon(linkAddon);
-	var cxReadFunc = null;
-	function writeData(buf)
-	{
-		term.write(new Uint8Array(buf));
-	}
-	function readData(str)
-	{
-		if(cxReadFunc == null)
-			return;
-		for(var i=0;i<str.length;i++)
-			cxReadFunc(str.charCodeAt(i));
-	}
-	function printMessage(msg)
-	{
-		for(var i=0;i<msg.length;i++)
-			term.write(msg[i] + "\n");
-	}
-	function hddCallback(state)
-	{
-		diskActivity.set(state != "ready");
-	}
-	function cpuCallback(state)
-	{
-		cpuActivity.set(state != "ready");
-	}
-	term.onData(readData);
-	function initTerminal()
-	{
-		const consoleDiv = document.getElementById("console");
-		term.open(consoleDiv);
-		term.scrollToTop();
-		fitAddon.fit();
-		window.addEventListener("resize", function(ev){ fitAddon.fit(); });
-		term.focus();
-		// Avoid undesired default DnD handling
-		function preventDefaults (e) {
-			e.preventDefault()
-			e.stopPropagation()
-		}
-		consoleDiv.addEventListener("dragover", preventDefaults, false);
-		consoleDiv.addEventListener("dragenter", preventDefaults, false);
-		consoleDiv.addEventListener("dragleave", preventDefaults, false);
-		consoleDiv.addEventListener("drop", preventDefaults, false);
-		printMessage(introMessage);
-		initCheerpX();
-	}
-	async function initCheerpX()
-	{
-		// TODO: Check for SAB support
-		var blockDevice = null;
-		switch(diskImageType)
-		{
-			case "cloud":
-				try
-				{
-					blockDevice = await CheerpX.CloudDevice.create(diskImageUrl);
-				}
-				catch(e)
-				{
-					// Report the failure and try again with plain HTTP
-					var wssProtocol = "wss:";
-					if(diskImageUrl.startsWith(wssProtocol))
-					{
-						// WebSocket protocol failed, try agin using plain HTTP
-						plausible("WS Disk failure");
-						blockDevice = await CheerpX.CloudDevice.create("https:" + diskImageUrl.substr(wssProtocol.length));
-					}
-					else
-					{
-						// No other recovery option
-						throw e;
-					}
-				}
-				break;
-			case "bytes":
-				blockDevice = await CheerpX.HttpBytesDevice.create(diskImageUrl);
-				break;
-			case "github":
-				blockDevice = await CheerpX.GitHubDevice.create(diskImageUrl);
-				break;
-			default:
-				throw new Error("Unrecognized device type");
-		}
-		var overlayDevice = await CheerpX.OverlayDevice.create(blockDevice, await CheerpX.IDBDevice.create("block1"));
-		var webDevice = await CheerpX.WebDevice.create("");
-		var dataDevice = await CheerpX.DataDevice.create();
-		var mountPoints = [
-			// The root filesystem, as an Ext2 image
-			{type:"ext2", dev:overlayDevice, path:"/"},
-			// Access to files on the Web server, relative to the current page
-			{type:"dir", dev:webDevice, path:"/web"},
-			// Access to read-only data coming from JavaScript
-			{type:"dir", dev:dataDevice, path:"/data"},
-			// Automatically created device files
-			{type:"devs", path:"/dev"},
-			// The Linux 'proc' filesystem which provides information about running processes
-			{type:"proc", path:"/proc"}
-		];
-		try
-		{
-			cx = await CheerpX.Linux.create({mounts: mountPoints, networkInterface: networkInterface});
-		}
-		catch(e)
-		{
-			printMessage(errorMessage);
-			printMessage([e.toString()]);
-			return;
-		}
-		cx.registerCallback("cpuActivity", cpuCallback);
-		cx.registerCallback("diskActivity", hddCallback);
-		term.scrollToBottom();
-		cxReadFunc = cx.setCustomConsole(writeData, term.cols, term.rows);
-		// Reasonable defaults for local deployments
-		// var cmd = "/bin/bash";
-		// var args = ["--login"];
-		// var env = ["HOME=/home/user", "TERM=xterm", "USER=user", "SHELL=/bin/bash", "EDITOR=vim", "LANG=en_US.UTF-8", "LC_ALL=C"];
-		// var cwd = "/home/user";
-		// Executable full path (Required)
-		var cmd = "/bin/bash";
-		// Arguments, as an array (Required)
-		var args = ["--login"]
-		// Optional extra parameters
-		var opts = {
-			// Environment variables
-			env: ["HOME=/home/user", "TERM=xterm", "USER=user", "SHELL=/bin/bash", "EDITOR=vim", "LANG=en_US.UTF-8", "LC_ALL=C"],
-			// Current working directory
-			cwd: "/home/user",
-			// User id
-			uid: 1000,
-			// Group id
-			gid: 1000
-		};
-		// Run the command in a loop, in case the user exits
-		while (true)
-		{
-			await cx.run(cmd, args, opts);
-		}
-	}
-	onMount(initTerminal);
-	async function handleConnect()
-	{
-		const w = window.open("login.html", "_blank");
-		await cx.networkLogin();
-		w.location.href = await startLogin();
-	}
+import WebVM from '$lib/WebVM.svelte';
+import { diskImageUrl, diskImageType } from 'diskConfig'
 </script>
 
-<main class="relative w-full h-full">
-	<Nav />
-	<div class="absolute top-10 bottom-0 left-0 right-0">
-		<SideBar on:connect={handleConnect}/>
-		<div class="absolute top-0 bottom-0 left-14 right-0 p-1 scrollbar" id="console">
-		</div>
-	</div>
-</main>
-
+<WebVM diskImageUrl={diskImageUrl} diskImageType={diskImageType} />