sketch: fix utf8 issues in terminal

There's some fun between the OS being UTF8 and JS strings
being UTF16. Using uint8arrays seems to help.
diff --git a/loop/webui/src/web-components/sketch-terminal.ts b/loop/webui/src/web-components/sketch-terminal.ts
index 8e4805e..78f1c12 100644
--- a/loop/webui/src/web-components/sketch-terminal.ts
+++ b/loop/webui/src/web-components/sketch-terminal.ts
@@ -92,7 +92,7 @@
 
       if (!response.ok) {
         console.error(
-          `Failed to load xterm CSS: ${response.status} ${response.statusText}`,
+          `Failed to load xterm CSS: ${response.status} ${response.statusText}`
         );
         return;
       }
@@ -117,7 +117,7 @@
    */
   public async initializeTerminal(): Promise<void> {
     const terminalContainer = this.renderRoot.querySelector(
-      "#terminalContainer",
+      "#terminalContainer"
     ) as HTMLElement;
 
     if (!terminalContainer) {
@@ -193,12 +193,11 @@
         if (this.terminal) {
           // Decode base64 data before writing to terminal
           try {
-            const decoded = atob(event.data);
+            // @ts-ignore This isn't in the type definitions yet; it's pretty new?!?
+            const decoded = base64ToUint8Array(event.data);
             this.terminal.write(decoded);
           } catch (e) {
             console.error("Error decoding terminal data:", e);
-            // Fallback to raw data if decoding fails
-            this.terminal.write(event.data);
           }
         }
       };
@@ -224,7 +223,7 @@
       console.error("Failed to connect to terminal:", error);
       if (this.terminal) {
         this.terminal.write(
-          `\r\n\x1b[1;31mFailed to connect: ${error}\x1b[0m\r\n`,
+          `\r\n\x1b[1;31mFailed to connect: ${error}\x1b[0m\r\n`
         );
       }
     }
@@ -284,12 +283,12 @@
           headers: {
             "Content-Type": "text/plain",
           },
-        },
+        }
       );
 
       if (!response.ok) {
         console.error(
-          `Failed to send terminal input: ${response.status} ${response.statusText}`,
+          `Failed to send terminal input: ${response.status} ${response.statusText}`
         );
       }
     } catch (error) {
@@ -325,12 +324,12 @@
           headers: {
             "Content-Type": "application/json",
           },
-        },
+        }
       );
 
       if (!response.ok) {
         console.error(
-          `Failed to send terminal resize: ${response.status} ${response.statusText}`,
+          `Failed to send terminal resize: ${response.status} ${response.statusText}`
         );
       }
     } catch (error) {
@@ -347,6 +346,18 @@
   }
 }
 
+function base64ToUint8Array(base64String) {
+  // This isn't yet available in Chrome, but Safari has it!
+  // @ts-ignore
+  if (Uint8Array.fromBase64) {
+    // @ts-ignore
+    return Uint8Array.fromBase64(base64String);
+  }
+
+  const binaryString = atob(base64String);
+  return Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
+}
+
 declare global {
   interface HTMLElementTagNameMap {
     "sketch-terminal": SketchTerminal;