From 80fc871a718fc1c7c9426fa57a0c144afb5e14ad Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Fri, 23 Feb 2024 16:37:09 +0000
Subject: [PATCH 001/106] Example

---
 src/editor/codemirror/CodeMirror.tsx          |  55 ++++++++--
 .../codemirror/reactWidgetExtension.tsx       | 103 ++++++++++++++++++
 2 files changed, 149 insertions(+), 9 deletions(-)
 create mode 100644 src/editor/codemirror/reactWidgetExtension.tsx

diff --git a/src/editor/codemirror/CodeMirror.tsx b/src/editor/codemirror/CodeMirror.tsx
index 31d155dd1..495ed9ef8 100644
--- a/src/editor/codemirror/CodeMirror.tsx
+++ b/src/editor/codemirror/CodeMirror.tsx
@@ -12,7 +12,15 @@ import {
   lineNumbers,
   ViewUpdate,
 } from "@codemirror/view";
-import { useEffect, useMemo, useRef } from "react";
+import React, {
+  ReactNode,
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { createPortal } from "react-dom";
 import { useIntl } from "react-intl";
 import { lineNumFromUint8Array } from "../../common/text-util";
 import useActionFeedback from "../../common/use-action-feedback";
@@ -40,6 +48,7 @@ import { languageServer } from "./language-server/view";
 import { lintGutter } from "./lint/lint";
 import { codeStructure } from "./structure-highlighting";
 import themeExtensions from "./themeExtensions";
+import { reactWidgetExtension } from "./reactWidgetExtension";
 
 interface CodeMirrorProps {
   className?: string;
@@ -52,6 +61,20 @@ interface CodeMirrorProps {
   parameterHelpOption: ParameterHelpOption;
 }
 
+interface PortalContent {
+  dom: HTMLElement;
+  content: ReactNode;
+}
+
+/**
+ * Creates a React portal for a CodeMirror dom element (e.g. for a widget) and
+ * returns a clean-up function to call when the widget is destroyed.
+ */
+export type PortalFactory = (
+  dom: HTMLElement,
+  content: ReactNode
+) => () => void;
+
 /**
  * A React component for CodeMirror 6.
  *
@@ -100,6 +123,13 @@ const CodeMirror = ({
     [fontSize, codeStructureOption, parameterHelpOption]
   );
 
+  const [portals, setPortals] = useState<PortalContent[]>([]);
+  const portalFactory: PortalFactory = useCallback((dom, content) => {
+    const portal = { dom, content };
+    setPortals((portals) => [...portals, portal]);
+    return () => setPortals((portals) => portals.filter((p) => p !== portal));
+  }, []);
+
   useEffect(() => {
     const initializing = !viewRef.current;
     if (initializing) {
@@ -118,6 +148,7 @@ const CodeMirror = ({
         extensions: [
           notify,
           editorConfig,
+          reactWidgetExtension(portalFactory),
           // Extension requires external state.
           dndSupport({ sessionSettings, setSessionSettings }),
           // Extensions only relevant for editing:
@@ -172,6 +203,9 @@ const CodeMirror = ({
     parameterHelpOption,
     uri,
     apiReferenceMap,
+    portals,
+    portalFactory,
+    setPortals,
   ]);
   useEffect(() => {
     // Do this separately as we don't want to destroy the view whenever options needed for initialization change.
@@ -260,13 +294,16 @@ const CodeMirror = ({
   }, [routerState, setRouterState]);
 
   return (
-    <section
-      data-testid="editor"
-      aria-label={intl.formatMessage({ id: "code-editor" })}
-      style={{ height: "100%" }}
-      className={className}
-      ref={elementRef}
-    />
+    <>
+      <section
+        data-testid="editor"
+        aria-label={intl.formatMessage({ id: "code-editor" })}
+        style={{ height: "100%" }}
+        className={className}
+        ref={elementRef}
+      />
+      {portals.map(({ content, dom }) => createPortal(content, dom))}
+    </>
   );
 };
 
@@ -293,4 +330,4 @@ const logPastedLineCount = (logging: Logging, update: ViewUpdate) => {
     );
 };
 
-export default CodeMirror;
+export default CodeMirror;
\ No newline at end of file
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
new file mode 100644
index 000000000..680ac2d17
--- /dev/null
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -0,0 +1,103 @@
+import { Button, HStack, Text } from "@chakra-ui/react";
+import { EditorState, Extension, StateField } from "@codemirror/state";
+import {
+  Decoration,
+  DecorationSet,
+  EditorView,
+  WidgetType,
+} from "@codemirror/view";
+import { useCallback } from "react";
+import { supportedLanguages, useSettings } from "../../settings/settings";
+import { PortalFactory } from "./CodeMirror";
+
+/**
+ * An example react component that we use inside a CodeMirror widget as
+ * a proof of concept.
+ */
+const ExampleReactComponent = () => {
+  // This is a weird thing to do in a CodeMirror widget but proves the point that
+  // we can use React features to communicate with the rest of the app.
+  const [settings, setSettings] = useSettings();
+  const handleClick = useCallback(() => {
+    let { languageId } = settings;
+    while (languageId === settings.languageId) {
+      languageId =
+        supportedLanguages[
+          Math.floor(Math.random() * supportedLanguages.length)
+        ].id;
+    }
+    setSettings({
+      ...settings,
+      languageId,
+    });
+  }, [settings, setSettings]);
+  return (
+    <HStack fontFamily="body" spacing={5} py={3}>
+      <Button onClick={handleClick}>Pick random UI language</Button>
+      <Text fontWeight="semibold">Current language: {settings.languageId}</Text>
+    </HStack>
+  );
+};
+
+/**
+ * This widget will have its contents rendered by the code in CodeMirror.tsx
+ * which it communicates with via the portal factory.
+ */
+class ExampleReactBlockWidget extends WidgetType {
+  private portalCleanup: (() => void) | undefined;
+
+  constructor(private createPortal: PortalFactory) {
+    super();
+  }
+
+  toDOM() {
+    const dom = document.createElement("div");
+    this.portalCleanup = this.createPortal(dom, <ExampleReactComponent />);
+    return dom;
+  }
+
+  destroy(dom: HTMLElement): void {
+    if (this.portalCleanup) {
+      this.portalCleanup();
+    }
+  }
+
+  ignoreEvent() {
+    return true;
+  }
+}
+
+/**
+ * A toy extension that creates a wiget after the first line.
+ */
+export const reactWidgetExtension = (
+  createPortal: PortalFactory
+): Extension => {
+  const decorate = (state: EditorState) => {
+    // Just put a widget at the start of the document.
+    // A more interesting example would look at the cursor (selection) and/or syntax tree.
+    const endOfFirstLine = state.doc.lineAt(0).to;
+    const widget = Decoration.widget({
+      block: true,
+      widget: new ExampleReactBlockWidget(createPortal),
+      side: 1,
+    });
+    return Decoration.set(widget.range(endOfFirstLine));
+  };
+
+  const stateField = StateField.define<DecorationSet>({
+    create(state) {
+      return decorate(state);
+    },
+    update(widgets, transaction) {
+      if (transaction.docChanged) {
+        return decorate(transaction.state);
+      }
+      return widgets.map(transaction.changes);
+    },
+    provide(field) {
+      return EditorView.decorations.from(field);
+    },
+  });
+  return [stateField];
+};
\ No newline at end of file

From abf06c59f77eb306f23f898220b310dd76f9153a Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Fri, 23 Feb 2024 19:12:06 +0000
Subject: [PATCH 002/106] This should popup by select_pixel and let you select
 pixels interactively

---
 src/editor/codemirror/CodeMirror.tsx          |  55 ++++++--
 .../codemirror/reactWidgetExtension.tsx       | 129 ++++++++++++++++++
 2 files changed, 175 insertions(+), 9 deletions(-)
 create mode 100644 src/editor/codemirror/reactWidgetExtension.tsx

diff --git a/src/editor/codemirror/CodeMirror.tsx b/src/editor/codemirror/CodeMirror.tsx
index 31d155dd1..495ed9ef8 100644
--- a/src/editor/codemirror/CodeMirror.tsx
+++ b/src/editor/codemirror/CodeMirror.tsx
@@ -12,7 +12,15 @@ import {
   lineNumbers,
   ViewUpdate,
 } from "@codemirror/view";
-import { useEffect, useMemo, useRef } from "react";
+import React, {
+  ReactNode,
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { createPortal } from "react-dom";
 import { useIntl } from "react-intl";
 import { lineNumFromUint8Array } from "../../common/text-util";
 import useActionFeedback from "../../common/use-action-feedback";
@@ -40,6 +48,7 @@ import { languageServer } from "./language-server/view";
 import { lintGutter } from "./lint/lint";
 import { codeStructure } from "./structure-highlighting";
 import themeExtensions from "./themeExtensions";
+import { reactWidgetExtension } from "./reactWidgetExtension";
 
 interface CodeMirrorProps {
   className?: string;
@@ -52,6 +61,20 @@ interface CodeMirrorProps {
   parameterHelpOption: ParameterHelpOption;
 }
 
+interface PortalContent {
+  dom: HTMLElement;
+  content: ReactNode;
+}
+
+/**
+ * Creates a React portal for a CodeMirror dom element (e.g. for a widget) and
+ * returns a clean-up function to call when the widget is destroyed.
+ */
+export type PortalFactory = (
+  dom: HTMLElement,
+  content: ReactNode
+) => () => void;
+
 /**
  * A React component for CodeMirror 6.
  *
@@ -100,6 +123,13 @@ const CodeMirror = ({
     [fontSize, codeStructureOption, parameterHelpOption]
   );
 
+  const [portals, setPortals] = useState<PortalContent[]>([]);
+  const portalFactory: PortalFactory = useCallback((dom, content) => {
+    const portal = { dom, content };
+    setPortals((portals) => [...portals, portal]);
+    return () => setPortals((portals) => portals.filter((p) => p !== portal));
+  }, []);
+
   useEffect(() => {
     const initializing = !viewRef.current;
     if (initializing) {
@@ -118,6 +148,7 @@ const CodeMirror = ({
         extensions: [
           notify,
           editorConfig,
+          reactWidgetExtension(portalFactory),
           // Extension requires external state.
           dndSupport({ sessionSettings, setSessionSettings }),
           // Extensions only relevant for editing:
@@ -172,6 +203,9 @@ const CodeMirror = ({
     parameterHelpOption,
     uri,
     apiReferenceMap,
+    portals,
+    portalFactory,
+    setPortals,
   ]);
   useEffect(() => {
     // Do this separately as we don't want to destroy the view whenever options needed for initialization change.
@@ -260,13 +294,16 @@ const CodeMirror = ({
   }, [routerState, setRouterState]);
 
   return (
-    <section
-      data-testid="editor"
-      aria-label={intl.formatMessage({ id: "code-editor" })}
-      style={{ height: "100%" }}
-      className={className}
-      ref={elementRef}
-    />
+    <>
+      <section
+        data-testid="editor"
+        aria-label={intl.formatMessage({ id: "code-editor" })}
+        style={{ height: "100%" }}
+        className={className}
+        ref={elementRef}
+      />
+      {portals.map(({ content, dom }) => createPortal(content, dom))}
+    </>
   );
 };
 
@@ -293,4 +330,4 @@ const logPastedLineCount = (logging: Logging, update: ViewUpdate) => {
     );
 };
 
-export default CodeMirror;
+export default CodeMirror;
\ No newline at end of file
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
new file mode 100644
index 000000000..58e4cbfc7
--- /dev/null
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -0,0 +1,129 @@
+import { Button, HStack, Text } from "@chakra-ui/react";
+import { EditorState, Extension, StateField } from "@codemirror/state";
+import {
+  Decoration,
+  DecorationSet,
+  EditorView,
+  WidgetType,
+} from "@codemirror/view";
+import { useCallback } from "react";
+import { supportedLanguages, useSettings } from "../../settings/settings";
+import { PortalFactory } from "./CodeMirror";
+
+/**
+ * An example react component that we use inside a CodeMirror widget as
+ * a proof of concept.
+ */
+const ExampleReactComponent = () => {
+  // This is a weird thing to do in a CodeMirror widget but proves the point that
+  // we can use React features to communicate with the rest of the app.
+  const [settings, setSettings] = useSettings();
+  const handleClick = useCallback(() => {
+    let { languageId } = settings;
+    while (languageId === settings.languageId) {
+      languageId =
+        supportedLanguages[
+          Math.floor(Math.random() * supportedLanguages.length)
+        ].id;
+    }
+    setSettings({
+      ...settings,
+      languageId,
+    });
+  }, [settings, setSettings]);
+  return (
+    <HStack fontFamily="body" spacing={5} py={3}>
+      <Button onClick={handleClick}>Pick random UI language</Button>
+      <Text fontWeight="semibold">Current language: {settings.languageId}</Text>
+    </HStack>
+  );
+};
+
+const MicrobitLEDSelector = () => {
+    const selectedLED = null; // Initially, no LED is selected
+    
+    return (
+      <div style={{ padding: "10px", border: "1px solid #ccc" }}>
+        <h4>Select lights to turn on</h4>
+        <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 20px)", gap: "5px" }}>
+          {[...Array(5)].map((_, row) => (
+            [...Array(5)].map((_, col) => (
+              <div
+                key={`${row},${col}`}
+                style={{
+                  width: "20px",
+                  height: "20px",
+                  border: "1px solid #ccc",
+                  background: "white", // Initially all LEDs are white
+                }}
+              ></div>
+            ))
+          ))}
+        </div>
+      </div>
+    );
+  };
+
+/**
+ * This widget will have its contents rendered by the code in CodeMirror.tsx
+ * which it communicates with via the portal factory.
+ */
+class ExampleReactBlockWidget extends WidgetType {
+  private portalCleanup: (() => void) | undefined;
+
+  constructor(private createPortal: PortalFactory) {
+    super();
+  }
+
+  toDOM() {
+    const dom = document.createElement("div");
+    this.portalCleanup = this.createPortal(dom, <MicrobitLEDSelector />);
+    return dom;
+  }
+
+  destroy(dom: HTMLElement): void {
+    if (this.portalCleanup) {
+      this.portalCleanup();
+    }
+  }
+
+  ignoreEvent() {
+    return true;
+  }
+}
+
+/**
+ * A toy extension that creates a wiget after the first line.
+ */
+export const reactWidgetExtension = (
+  createPortal: PortalFactory
+): Extension => {
+  const decorate = (state: EditorState) => {
+    // Just put a widget at the start of the document.
+    // A more interesting example would look at the cursor (selection) and/or syntax tree.
+    const endOfFirstLine = state.doc.lineAt(0).to;
+    const widget = Decoration.widget({
+      block: true,
+      widget: new ExampleReactBlockWidget(createPortal),
+      side: 1,
+    });
+    
+    return Decoration.set(widget.range(endOfFirstLine));
+  };
+
+  const stateField = StateField.define<DecorationSet>({
+    create(state) {
+      return decorate(state);
+    },
+    update(widgets, transaction) {
+      if (transaction.docChanged) {
+        return decorate(transaction.state);
+      }
+      return widgets.map(transaction.changes);
+    },
+    provide(field) {
+      return EditorView.decorations.from(field);
+    },
+  });
+  return [stateField];
+};
\ No newline at end of file

From 2eef8a968c7d111f495ffb434a8fccd659be50c0 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 25 Feb 2024 18:48:11 +0000
Subject: [PATCH 003/106] modified to create incr button

---
 .../codemirror/reactWidgetExtension.tsx       | 24 +++++++------------
 1 file changed, 8 insertions(+), 16 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 680ac2d17..41efed6be 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -6,7 +6,7 @@ import {
   EditorView,
   WidgetType,
 } from "@codemirror/view";
-import { useCallback } from "react";
+import { useState, useCallback } from "react";
 import { supportedLanguages, useSettings } from "../../settings/settings";
 import { PortalFactory } from "./CodeMirror";
 
@@ -17,24 +17,16 @@ import { PortalFactory } from "./CodeMirror";
 const ExampleReactComponent = () => {
   // This is a weird thing to do in a CodeMirror widget but proves the point that
   // we can use React features to communicate with the rest of the app.
-  const [settings, setSettings] = useSettings();
+  // Use the useState hook to store and update the counter value.
+  const [counter, setCounter] = useState(0);
+  // Define a callback function that increments the counter by one.
   const handleClick = useCallback(() => {
-    let { languageId } = settings;
-    while (languageId === settings.languageId) {
-      languageId =
-        supportedLanguages[
-          Math.floor(Math.random() * supportedLanguages.length)
-        ].id;
-    }
-    setSettings({
-      ...settings,
-      languageId,
-    });
-  }, [settings, setSettings]);
+    setCounter(counter + 1);
+  }, [counter]);
   return (
     <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleClick}>Pick random UI language</Button>
-      <Text fontWeight="semibold">Current language: {settings.languageId}</Text>
+      <Button onClick={handleClick}>Increment</Button>
+      <Text fontWeight="semibold">Counter: {counter}</Text>
     </HStack>
   );
 };

From 8d9650a0f0bc3f28e35854452b1d2b098fabe818 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Wed, 28 Feb 2024 13:22:46 +0000
Subject: [PATCH 004/106] increment button for each comment

iterates through syntax tree, printing node.name to console
---
 .../codemirror/reactWidgetExtension.tsx       | 39 ++++++++++++++-----
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 41efed6be..082bbf8dc 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -6,9 +6,11 @@ import {
   EditorView,
   WidgetType,
 } from "@codemirror/view";
+import { syntaxTree } from "@codemirror/language"
 import { useState, useCallback } from "react";
 import { supportedLanguages, useSettings } from "../../settings/settings";
 import { PortalFactory } from "./CodeMirror";
+import { debug } from "../../editor/codemirror/dnd";
 
 /**
  * An example react component that we use inside a CodeMirror widget as
@@ -22,6 +24,7 @@ const ExampleReactComponent = () => {
   // Define a callback function that increments the counter by one.
   const handleClick = useCallback(() => {
     setCounter(counter + 1);
+    //console.log(counter)
   }, [counter]);
   return (
     <HStack fontFamily="body" spacing={5} py={3}>
@@ -66,15 +69,33 @@ export const reactWidgetExtension = (
   createPortal: PortalFactory
 ): Extension => {
   const decorate = (state: EditorState) => {
-    // Just put a widget at the start of the document.
-    // A more interesting example would look at the cursor (selection) and/or syntax tree.
-    const endOfFirstLine = state.doc.lineAt(0).to;
-    const widget = Decoration.widget({
-      block: true,
-      widget: new ExampleReactBlockWidget(createPortal),
-      side: 1,
-    });
-    return Decoration.set(widget.range(endOfFirstLine));
+    let widgets: any[] = []
+    let from = 0
+    let to = 100
+    
+    syntaxTree(state).iterate({
+      from, to,
+      enter: (node: any) => {
+        console.log(node.name)
+        if (node.name == "Comment") {
+          let isTrue = state.doc.sliceString(node.from, node.to) == "true"
+          let deco = Decoration.widget({
+            widget: new ExampleReactBlockWidget(createPortal),
+            side: 1,
+          });
+          widgets.push(deco.range(node.to))
+        }
+      }
+    })
+    return Decoration.set(widgets)
+    
+    // const endOfFirstLine = state.doc.lineAt(0).to;
+    // const widget = Decoration.widget({
+    //   block: true,
+    //   widget: new ExampleReactBlockWidget(createPortal),
+    //   side: 1,
+    // });
+    // return Decoration.set(widget.range(endOfFirstLine));
   };
 
   const stateField = StateField.define<DecorationSet>({

From 452e5a557044d7108eda6c7d94b5c16a6ccfcc77 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Wed, 28 Feb 2024 13:44:56 +0000
Subject: [PATCH 005/106] print doc as string to console

---
 src/editor/codemirror/reactWidgetExtension.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 082bbf8dc..16e331c88 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -72,7 +72,8 @@ export const reactWidgetExtension = (
     let widgets: any[] = []
     let from = 0
     let to = 100
-    
+    let t = state.doc.toString()
+    console.log(t);
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => {

From 7f6f08eba175352bde63fb362e93b162077b54c5 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Wed, 28 Feb 2024 13:59:41 +0000
Subject: [PATCH 006/106] button after all SoundEffect mentions

---
 src/editor/codemirror/reactWidgetExtension.tsx | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 16e331c88..d592bf5ff 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -71,15 +71,16 @@ export const reactWidgetExtension = (
   const decorate = (state: EditorState) => {
     let widgets: any[] = []
     let from = 0
-    let to = 100
+    let to = 10000 // TODO: modify this to be the actual end
     let t = state.doc.toString()
     console.log(t);
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => {
         console.log(node.name)
-        if (node.name == "Comment") {
-          let isTrue = state.doc.sliceString(node.from, node.to) == "true"
+        console.log(state.doc.sliceString(node.from, node.to))
+        if (node.name == "VariableName" && state.doc.sliceString(node.from, node.to) == "SoundEffect") {
+          //let isTrue = state.doc.sliceString(node.from, node.to) == "true"
           let deco = Decoration.widget({
             widget: new ExampleReactBlockWidget(createPortal),
             side: 1,

From dfc2bc24d2cbd82aadf6ad09da63f47620e95166 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Wed, 28 Feb 2024 14:17:05 +0000
Subject: [PATCH 007/106] only puts incr button after SoundEffect ArgList

seems to have intended effect, but might not be best solution
---
 src/editor/codemirror/reactWidgetExtension.tsx | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index d592bf5ff..0ef389500 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -73,13 +73,14 @@ export const reactWidgetExtension = (
     let from = 0
     let to = 10000 // TODO: modify this to be the actual end
     let t = state.doc.toString()
+    let sound = false
     console.log(t);
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => {
-        console.log(node.name)
-        console.log(state.doc.sliceString(node.from, node.to))
-        if (node.name == "VariableName" && state.doc.sliceString(node.from, node.to) == "SoundEffect") {
+        //console.log(node.name)
+        //console.log(state.doc.sliceString(node.from, node.to))
+        if(sound && node.name == "ArgList"){
           //let isTrue = state.doc.sliceString(node.from, node.to) == "true"
           let deco = Decoration.widget({
             widget: new ExampleReactBlockWidget(createPortal),
@@ -87,6 +88,9 @@ export const reactWidgetExtension = (
           });
           widgets.push(deco.range(node.to))
         }
+        // detected SoundEffect, if next expression is an ArgList, show UI
+        // TODO: ensure this is the only case of SoundEffect ArgList
+        sound = node.name == "VariableName" && state.doc.sliceString(node.from, node.to) == "SoundEffect"
       }
     })
     return Decoration.set(widgets)

From e2ae08a93e5f36abdc428200156c3d2383071b85 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 3 Mar 2024 17:16:14 +0000
Subject: [PATCH 008/106] decorate now iterates syntax tree to end of file

---
 src/editor/codemirror/reactWidgetExtension.tsx | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 0ef389500..aaf6b25f3 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -62,16 +62,14 @@ class ExampleReactBlockWidget extends WidgetType {
   }
 }
 
-/**
- * A toy extension that creates a wiget after the first line.
- */
+// Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
 ): Extension => {
   const decorate = (state: EditorState) => {
     let widgets: any[] = []
     let from = 0
-    let to = 10000 // TODO: modify this to be the actual end
+    let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
     let t = state.doc.toString()
     let sound = false
     console.log(t);

From d3d0a001fba666afb78beedb63df9cf8f3222ede Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 3 Mar 2024 17:51:29 +0000
Subject: [PATCH 009/106] begin parsing arguments in SoundEffect

---
 .../codemirror/reactWidgetExtension.tsx       | 53 ++++++++++++++-----
 1 file changed, 41 insertions(+), 12 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index aaf6b25f3..b70b4ef50 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -71,24 +71,53 @@ export const reactWidgetExtension = (
     let from = 0
     let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
     let t = state.doc.toString()
-    let sound = false
-    console.log(t);
+    //console.log(t);
+
+    let sound = false // detected a SoundEffect, waiting to pair with ArgList
+    let parsingArgs = false; let argEnd = 0; // Found pattern to create widget, parse args
+    // I: parenthesis = # open paren - # closed paren, G: parenthesis <= 0
+    // !G ^ I -> # open paren - # closed paren = 0, reached end of ArgList 
+    let parenthesis = 0; 
+
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => {
         //console.log(node.name)
         //console.log(state.doc.sliceString(node.from, node.to))
-        if(sound && node.name == "ArgList"){
-          //let isTrue = state.doc.sliceString(node.from, node.to) == "true"
-          let deco = Decoration.widget({
-            widget: new ExampleReactBlockWidget(createPortal),
-            side: 1,
-          });
-          widgets.push(deco.range(node.to))
+
+        // Walking through ArgList, trying to match with values
+        if(parsingArgs){
+          if(node.name == "(") parenthesis += 1
+          else if(node.name == ")") {
+            parenthesis -= 1
+            if(parenthesis <= 0){
+              // finished parsing ArgList
+              parsingArgs = false
+    
+              // create the widget
+              let deco = Decoration.widget({
+                widget: new ExampleReactBlockWidget(createPortal),
+                side: 1,
+              });
+              widgets.push(deco.range(argEnd))
+            }
+          }
+          else{
+            console.log(node.name)
+            console.log(state.doc.sliceString(node.from, node.to))
+            // Claim: if parenthesis = 1 (and code is well formatted) then we are not nested 
+            // Idea: parse through, adding name and slices until a comma is spotted at parenthesis = 1
+            // at the very end, we can figure out if they are valid, and therefore if to modify the visual
+          }
+        }
+        else{
+          // Found ArgList, will begin to parse nodes 
+          if(sound && node.name == "ArgList") { sound = false; parsingArgs = true; argEnd = node.to }
+
+          // detected SoundEffect, if next expression is an ArgList, show UI
+          // TODO: ensure this is the only case of SoundEffect ArgList
+          sound = node.name == "VariableName" && state.doc.sliceString(node.from, node.to) == "SoundEffect"
         }
-        // detected SoundEffect, if next expression is an ArgList, show UI
-        // TODO: ensure this is the only case of SoundEffect ArgList
-        sound = node.name == "VariableName" && state.doc.sliceString(node.from, node.to) == "SoundEffect"
       }
     })
     return Decoration.set(widgets)

From 1af8bf283a5488eb6824b7d39392aca081231248 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 5 Mar 2024 13:44:17 +0000
Subject: [PATCH 010/106] fixed issues

---
 src/editor/codemirror/reactWidgetExtension.tsx | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index b70b4ef50..2714e7e66 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -8,9 +8,7 @@ import {
 } from "@codemirror/view";
 import { syntaxTree } from "@codemirror/language"
 import { useState, useCallback } from "react";
-import { supportedLanguages, useSettings } from "../../settings/settings";
 import { PortalFactory } from "./CodeMirror";
-import { debug } from "../../editor/codemirror/dnd";
 
 /**
  * An example react component that we use inside a CodeMirror widget as
@@ -70,7 +68,7 @@ export const reactWidgetExtension = (
     let widgets: any[] = []
     let from = 0
     let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
-    let t = state.doc.toString()
+    //let t = state.doc.toString()
     //console.log(t);
 
     let sound = false // detected a SoundEffect, waiting to pair with ArgList
@@ -87,8 +85,8 @@ export const reactWidgetExtension = (
 
         // Walking through ArgList, trying to match with values
         if(parsingArgs){
-          if(node.name == "(") parenthesis += 1
-          else if(node.name == ")") {
+          if(node.name === "(") parenthesis += 1
+          else if(node.name === ")") {
             parenthesis -= 1
             if(parenthesis <= 0){
               // finished parsing ArgList
@@ -112,11 +110,11 @@ export const reactWidgetExtension = (
         }
         else{
           // Found ArgList, will begin to parse nodes 
-          if(sound && node.name == "ArgList") { sound = false; parsingArgs = true; argEnd = node.to }
+          if(sound && node.name === "ArgList") { sound = false; parsingArgs = true; argEnd = node.to }
 
           // detected SoundEffect, if next expression is an ArgList, show UI
           // TODO: ensure this is the only case of SoundEffect ArgList
-          sound = node.name == "VariableName" && state.doc.sliceString(node.from, node.to) == "SoundEffect"
+          sound = node.name === "VariableName" && state.doc.sliceString(node.from, node.to) === "SoundEffect"
         }
       }
     })

From 140997b5a1cadb1db51a503e4c5980f663dd23a7 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 5 Mar 2024 16:57:48 +0000
Subject: [PATCH 011/106] I'm proud, I would just need help with connecting
 this to editorstate logic and so on so it can appear when a user clicks on
 set_pixel, on the correct line, more like a popover and updates the
 parameters to the set_pixel function accordingly

---
 src/editor/codemirror/microbitWidget.tsx      | 132 ++++++++++++++++++
 .../codemirror/reactWidgetExtension.tsx       |  62 +-------
 2 files changed, 136 insertions(+), 58 deletions(-)
 create mode 100644 src/editor/codemirror/microbitWidget.tsx

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
new file mode 100644
index 000000000..e92d7ac4b
--- /dev/null
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -0,0 +1,132 @@
+import { Box, Grid, GridItem, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
+import { useState } from "react";
+
+interface Pixel {
+  x: number;
+  y: number;
+  brightness: number;
+}
+
+const MicrobitPixel: React.FC<{ brightness: number; selected: boolean; onClick: () => void }> = ({ brightness, selected, onClick }) => {
+  return (
+    <Button
+      size="sm"
+      h="20px"
+      w="20px"
+      p={0}
+      bgColor={selected ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(0, 0, 0, 1)"}
+      _hover={{ bgColor: selected ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(0, 0, 0, 1)" }}
+      onClick={onClick}
+      _focus={{ boxShadow: "none" }}
+      _active={{ bgColor: selected ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(0, 0, 0, 1)" }}
+    />
+  );
+};
+
+interface MicrobitGridProps {
+  onClickPixel: (pixel: Pixel) => void;
+  onSubmit: () => void;
+  isVisible: boolean;
+}
+
+const MicrobitGrid: React.FC<MicrobitGridProps> = ({ onClickPixel, onSubmit, isVisible }) => {
+  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+  const [brightness, setBrightness] = useState<number>(5);
+
+  const handleClickPixel = (x: number, y: number) => {
+    const newPixel: Pixel = { x, y, brightness };
+    setSelectedPixel(newPixel);
+    onClickPixel(newPixel);
+  };
+
+  const handleSliderChange = (value: number) => {
+    if (selectedPixel) {
+      setBrightness(value);
+      const updatedPixel: Pixel = { ...selectedPixel, brightness: value };
+      onClickPixel(updatedPixel);
+    }
+  };
+
+  const handleOkClick = () => {
+    onSubmit();
+  };
+
+  return (
+    <Box display={isVisible ? "flex" : "none"} flexDirection="row">
+      <Box>
+        <Grid templateColumns={`repeat(5, 1fr)`} gap="2px" maxW="110px">
+          {[...Array(5)].map((_, x) => (
+            <GridItem key={x}>
+              <Grid templateColumns={`repeat(1, 1fr)`} gap="2px">
+                {[...Array(5)].map((_, y) => (
+                  <GridItem key={y}>
+                    <MicrobitPixel
+                      brightness={selectedPixel?.x === x && selectedPixel.y === y ? brightness : 0}
+                      selected={selectedPixel?.x === x && selectedPixel.y === y}
+                      onClick={() => handleClickPixel(x, y)}
+                    />
+                  </GridItem>
+                ))}
+              </Grid>
+            </GridItem>
+          ))}
+        </Grid>
+      </Box>
+      {selectedPixel && (
+        <Box ml="60px" mt="10px">
+          <Slider
+            aria-label="brightness"
+            defaultValue={brightness}
+            min={1}
+            max={9}
+            step={1}
+            onChange={handleSliderChange}
+            orientation="vertical"
+            _focus={{ boxShadow: "none" }}
+            _active={{ bgColor: "transparent" }}
+          >
+            <SliderTrack>
+              <SliderFilledTrack />
+            </SliderTrack>
+            <SliderThumb />
+          </Slider>
+        </Box>
+      )}
+      {selectedPixel && (
+        <Box display="flex" justifyContent="center" ml="10px" mt="100px">
+          <Button onClick={handleOkClick} colorScheme="blue" size="sm">
+            Looks Good!
+          </Button>
+        </Box>
+      )}
+    </Box>
+  );
+};
+
+export const MicrobitComponent: React.FC = () => {
+  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+  const [isVisible, setIsVisible] = useState(true);
+
+  const handleSelectPixel = (pixel: Pixel) => {
+    setSelectedPixel(pixel);
+  };
+
+  const handleSubmit = () => {
+    setIsVisible(false);
+    // Implement submit logic here
+    console.log("Submit button clicked");
+  };
+
+  return (
+    <Box>
+      {isVisible && (
+        <MicrobitGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />
+      )}
+      {selectedPixel && isVisible && (
+        <Box mt="4px">
+          Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) - Brightness: {selectedPixel.brightness}
+        </Box>
+      )}
+    </Box>
+  );
+};
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 58e4cbfc7..ab21dd285 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -1,4 +1,3 @@
-import { Button, HStack, Text } from "@chakra-ui/react";
 import { EditorState, Extension, StateField } from "@codemirror/state";
 import {
   Decoration,
@@ -6,63 +5,9 @@ import {
   EditorView,
   WidgetType,
 } from "@codemirror/view";
-import { useCallback } from "react";
-import { supportedLanguages, useSettings } from "../../settings/settings";
 import { PortalFactory } from "./CodeMirror";
+import { MicrobitComponent } from "./microbitWidget";
 
-/**
- * An example react component that we use inside a CodeMirror widget as
- * a proof of concept.
- */
-const ExampleReactComponent = () => {
-  // This is a weird thing to do in a CodeMirror widget but proves the point that
-  // we can use React features to communicate with the rest of the app.
-  const [settings, setSettings] = useSettings();
-  const handleClick = useCallback(() => {
-    let { languageId } = settings;
-    while (languageId === settings.languageId) {
-      languageId =
-        supportedLanguages[
-          Math.floor(Math.random() * supportedLanguages.length)
-        ].id;
-    }
-    setSettings({
-      ...settings,
-      languageId,
-    });
-  }, [settings, setSettings]);
-  return (
-    <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleClick}>Pick random UI language</Button>
-      <Text fontWeight="semibold">Current language: {settings.languageId}</Text>
-    </HStack>
-  );
-};
-
-const MicrobitLEDSelector = () => {
-    const selectedLED = null; // Initially, no LED is selected
-    
-    return (
-      <div style={{ padding: "10px", border: "1px solid #ccc" }}>
-        <h4>Select lights to turn on</h4>
-        <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 20px)", gap: "5px" }}>
-          {[...Array(5)].map((_, row) => (
-            [...Array(5)].map((_, col) => (
-              <div
-                key={`${row},${col}`}
-                style={{
-                  width: "20px",
-                  height: "20px",
-                  border: "1px solid #ccc",
-                  background: "white", // Initially all LEDs are white
-                }}
-              ></div>
-            ))
-          ))}
-        </div>
-      </div>
-    );
-  };
 
 /**
  * This widget will have its contents rendered by the code in CodeMirror.tsx
@@ -77,7 +22,7 @@ class ExampleReactBlockWidget extends WidgetType {
 
   toDOM() {
     const dom = document.createElement("div");
-    this.portalCleanup = this.createPortal(dom, <MicrobitLEDSelector />);
+    this.portalCleanup = this.createPortal(dom, <MicrobitComponent />);
     return dom;
   }
 
@@ -126,4 +71,5 @@ export const reactWidgetExtension = (
     },
   });
   return [stateField];
-};
\ No newline at end of file
+};
+

From cd9c53e74cf7668debdb15c23d85efe2a9064786 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Mon, 11 Mar 2024 16:47:20 +0000
Subject: [PATCH 012/106] refactored for clarity

---
 .../codemirror/reactWidgetExtension.tsx       | 59 +++++++------------
 1 file changed, 20 insertions(+), 39 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 2714e7e66..e9a7d3e07 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -36,7 +36,7 @@ const ExampleReactComponent = () => {
  * This widget will have its contents rendered by the code in CodeMirror.tsx
  * which it communicates with via the portal factory.
  */
-class ExampleReactBlockWidget extends WidgetType {
+class IncrementWidget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
   constructor(private createPortal: PortalFactory) {
@@ -60,6 +60,17 @@ class ExampleReactBlockWidget extends WidgetType {
   }
 }
 
+function createWidget(node: any, createPortal: PortalFactory): Decoration {
+  console.log(node[1]);
+  
+  let deco = Decoration.widget({
+    widget: new IncrementWidget(createPortal),
+    side: 1,
+  });
+
+  return deco;
+}
+
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
@@ -72,50 +83,20 @@ export const reactWidgetExtension = (
     //console.log(t);
 
     let sound = false // detected a SoundEffect, waiting to pair with ArgList
-    let parsingArgs = false; let argEnd = 0; // Found pattern to create widget, parse args
-    // I: parenthesis = # open paren - # closed paren, G: parenthesis <= 0
-    // !G ^ I -> # open paren - # closed paren = 0, reached end of ArgList 
-    let parenthesis = 0; 
 
     syntaxTree(state).iterate({
       from, to,
-      enter: (node: any) => {
+      enter: (node: any) => { // TODO: type is SyntaxNode
         //console.log(node.name)
         //console.log(state.doc.sliceString(node.from, node.to))
+        console.log(node.name);
+        console.log(node.node.getChildren());
 
-        // Walking through ArgList, trying to match with values
-        if(parsingArgs){
-          if(node.name === "(") parenthesis += 1
-          else if(node.name === ")") {
-            parenthesis -= 1
-            if(parenthesis <= 0){
-              // finished parsing ArgList
-              parsingArgs = false
-    
-              // create the widget
-              let deco = Decoration.widget({
-                widget: new ExampleReactBlockWidget(createPortal),
-                side: 1,
-              });
-              widgets.push(deco.range(argEnd))
-            }
-          }
-          else{
-            console.log(node.name)
-            console.log(state.doc.sliceString(node.from, node.to))
-            // Claim: if parenthesis = 1 (and code is well formatted) then we are not nested 
-            // Idea: parse through, adding name and slices until a comma is spotted at parenthesis = 1
-            // at the very end, we can figure out if they are valid, and therefore if to modify the visual
-          }
-        }
-        else{
-          // Found ArgList, will begin to parse nodes 
-          if(sound && node.name === "ArgList") { sound = false; parsingArgs = true; argEnd = node.to }
-
-          // detected SoundEffect, if next expression is an ArgList, show UI
-          // TODO: ensure this is the only case of SoundEffect ArgList
-          sound = node.name === "VariableName" && state.doc.sliceString(node.from, node.to) === "SoundEffect"
-        }
+        // Found ArgList, will begin to parse nodes 
+        if(sound && node.name === "ArgList") widgets.push(createWidget(node, createPortal).range(node.to));
+          
+        // detected SoundEffect, if next expression is an ArgList, show UI
+        sound = node.name === "VariableName" && state.doc.sliceString(node.from, node.to) === "SoundEffect"
       }
     })
     return Decoration.set(widgets)

From 50d0d65240f52d8371719ab5b86d1ac968dbd157 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Mon, 11 Mar 2024 16:53:27 +0000
Subject: [PATCH 013/106] initial implementation

---
 src/editor/codemirror/reactWidgetExtension.tsx | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index e9a7d3e07..ffaf4d92c 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -60,9 +60,9 @@ class IncrementWidget extends WidgetType {
   }
 }
 
-function createWidget(node: any, createPortal: PortalFactory): Decoration {
-  console.log(node[1]);
-  
+function createWidget(bool: string, from: number, to: number, createPortal: PortalFactory): Decoration {
+  console.log(bool);
+
   let deco = Decoration.widget({
     widget: new IncrementWidget(createPortal),
     side: 1,
@@ -82,21 +82,15 @@ export const reactWidgetExtension = (
     //let t = state.doc.toString()
     //console.log(t);
 
-    let sound = false // detected a SoundEffect, waiting to pair with ArgList
-
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => { // TODO: type is SyntaxNode
         //console.log(node.name)
         //console.log(state.doc.sliceString(node.from, node.to))
-        console.log(node.name);
-        console.log(node.node.getChildren());
 
-        // Found ArgList, will begin to parse nodes 
-        if(sound && node.name === "ArgList") widgets.push(createWidget(node, createPortal).range(node.to));
-          
-        // detected SoundEffect, if next expression is an ArgList, show UI
-        sound = node.name === "VariableName" && state.doc.sliceString(node.from, node.to) === "SoundEffect"
+        if(node.name === "Boolean") {
+          createWidget(state.doc.sliceString(node.from, node.to), node.from, node.to, createPortal)
+        }
       }
     })
     return Decoration.set(widgets)

From 2639675e13d59f3fff2079230b449930c1ab7ece Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Mon, 11 Mar 2024 16:55:35 +0000
Subject: [PATCH 014/106] Update reactWidgetExtension.tsx

---
 src/editor/codemirror/reactWidgetExtension.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index ffaf4d92c..01c3af37c 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -93,6 +93,7 @@ export const reactWidgetExtension = (
         }
       }
     })
+
     return Decoration.set(widgets)
     
     // const endOfFirstLine = state.doc.lineAt(0).to;

From 9a38948faa843b1eee438f3f6461305b48af8816 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Mon, 11 Mar 2024 17:08:35 +0000
Subject: [PATCH 015/106] temp

---
 src/editor/codemirror/reactWidgetExtension.tsx | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 01c3af37c..b21a50a59 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -14,10 +14,7 @@ import { PortalFactory } from "./CodeMirror";
  * An example react component that we use inside a CodeMirror widget as
  * a proof of concept.
  */
-const ExampleReactComponent = () => {
-  // This is a weird thing to do in a CodeMirror widget but proves the point that
-  // we can use React features to communicate with the rest of the app.
-  // Use the useState hook to store and update the counter value.
+function ToggleReactComponent(bval: boolean): React.ReactNode {
   const [counter, setCounter] = useState(0);
   // Define a callback function that increments the counter by one.
   const handleClick = useCallback(() => {
@@ -36,16 +33,17 @@ const ExampleReactComponent = () => {
  * This widget will have its contents rendered by the code in CodeMirror.tsx
  * which it communicates with via the portal factory.
  */
-class IncrementWidget extends WidgetType {
+class ToggleWidget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private createPortal: PortalFactory) {
+  constructor(private bval: boolean, private createPortal: PortalFactory) {
     super();
   }
 
   toDOM() {
+    console.log(this.bval);
     const dom = document.createElement("div");
-    this.portalCleanup = this.createPortal(dom, <ExampleReactComponent />);
+    this.portalCleanup = this.createPortal(dom, ToggleReactComponent(this.bval));
     return dom;
   }
 
@@ -61,10 +59,10 @@ class IncrementWidget extends WidgetType {
 }
 
 function createWidget(bool: string, from: number, to: number, createPortal: PortalFactory): Decoration {
-  console.log(bool);
+  let bval = bool === "True"
 
   let deco = Decoration.widget({
-    widget: new IncrementWidget(createPortal),
+    widget: new ToggleWidget(bval, createPortal),
     side: 1,
   });
 
@@ -89,7 +87,7 @@ export const reactWidgetExtension = (
         //console.log(state.doc.sliceString(node.from, node.to))
 
         if(node.name === "Boolean") {
-          createWidget(state.doc.sliceString(node.from, node.to), node.from, node.to, createPortal)
+          widgets.push(createWidget(state.doc.sliceString(node.from, node.to), node.from, node.to, createPortal).range(node.to));
         }
       }
     })

From d040fddec3c53d31f51ab5f4ff9b7955609a2d33 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Mon, 11 Mar 2024 17:15:33 +0000
Subject: [PATCH 016/106] Update reactWidgetExtension.tsx

---
 src/editor/codemirror/reactWidgetExtension.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index b21a50a59..e909437d2 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -14,6 +14,7 @@ import { PortalFactory } from "./CodeMirror";
  * An example react component that we use inside a CodeMirror widget as
  * a proof of concept.
  */
+
 function ToggleReactComponent(bval: boolean): React.ReactNode {
   const [counter, setCounter] = useState(0);
   // Define a callback function that increments the counter by one.

From dba517a2f75c3c491fe2a6a7b1cc9a7db3593d2e Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Mon, 11 Mar 2024 17:34:13 +0000
Subject: [PATCH 017/106] passing arguments into react widgets

---
 .../codemirror/reactWidgetExtension.tsx       | 22 ++++++++++---------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index e909437d2..192d89a6e 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -15,17 +15,15 @@ import { PortalFactory } from "./CodeMirror";
  * a proof of concept.
  */
 
-function ToggleReactComponent(bval: boolean): React.ReactNode {
-  const [counter, setCounter] = useState(0);
-  // Define a callback function that increments the counter by one.
+const ToggleReactComponent = ({ bval }: { bval: boolean }) => {
+  let x = bval ? "True" : "False"
   const handleClick = useCallback(() => {
-    setCounter(counter + 1);
-    //console.log(counter)
-  }, [counter]);
+    console.log();
+  }, []);
   return (
     <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleClick}>Increment</Button>
-      <Text fontWeight="semibold">Counter: {counter}</Text>
+      <Button onClick={handleClick}>Toggle</Button>
+      <Text fontWeight="semibold">Value: {x}</Text>
     </HStack>
   );
 };
@@ -44,7 +42,11 @@ class ToggleWidget extends WidgetType {
   toDOM() {
     console.log(this.bval);
     const dom = document.createElement("div");
-    this.portalCleanup = this.createPortal(dom, ToggleReactComponent(this.bval));
+    const handleClick = () => {
+      console.log("hi");
+    };
+
+    this.portalCleanup = this.createPortal(dom, < ToggleReactComponent bval={this.bval} />);
     return dom;
   }
 
@@ -86,7 +88,7 @@ export const reactWidgetExtension = (
       enter: (node: any) => { // TODO: type is SyntaxNode
         //console.log(node.name)
         //console.log(state.doc.sliceString(node.from, node.to))
-
+        //state.replaceSelection()
         if(node.name === "Boolean") {
           widgets.push(createWidget(state.doc.sliceString(node.from, node.to), node.from, node.to, createPortal).range(node.to));
         }

From 10213a5c0d55f47fb07e19799352147c2b8d66da Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 12 Mar 2024 02:13:43 -0400
Subject: [PATCH 018/106] widget reads values, needs view to dispatch changes

---
 .../codemirror/reactWidgetExtension.tsx       | 56 ++++++++++++++++---
 1 file changed, 49 insertions(+), 7 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 192d89a6e..82b72d161 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -40,11 +40,7 @@ class ToggleWidget extends WidgetType {
   }
 
   toDOM() {
-    console.log(this.bval);
     const dom = document.createElement("div");
-    const handleClick = () => {
-      console.log("hi");
-    };
 
     this.portalCleanup = this.createPortal(dom, < ToggleReactComponent bval={this.bval} />);
     return dom;
@@ -61,6 +57,40 @@ class ToggleWidget extends WidgetType {
   }
 }
 
+
+const TextComponent = () => {
+  return (
+    <HStack fontFamily="body" spacing={5} py={3}>
+      <Text fontWeight="semibold">False</Text>
+    </HStack>
+  );
+};
+
+class TextWidget extends WidgetType {
+  private portalCleanup: (() => void) | undefined;
+
+  constructor(private createPortal: PortalFactory) {
+    super();
+  }
+
+  toDOM() {
+    const dom = document.createElement("div");
+
+    this.portalCleanup = this.createPortal(dom, < TextComponent />);
+    return dom;
+  }
+
+  destroy(dom: HTMLElement): void {
+    if (this.portalCleanup) {
+      this.portalCleanup();
+    }
+  }
+
+  ignoreEvent() {
+    return true;
+  }
+}
+
 function createWidget(bool: string, from: number, to: number, createPortal: PortalFactory): Decoration {
   let bval = bool === "True"
 
@@ -86,10 +116,22 @@ export const reactWidgetExtension = (
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => { // TODO: type is SyntaxNode
-        //console.log(node.name)
-        //console.log(state.doc.sliceString(node.from, node.to))
-        //state.replaceSelection()
         if(node.name === "Boolean") {
+          // view.dispatch({
+          //   changes: {
+          //     from: node.from,
+          //     to: node.to,
+          //     insert: state.doc.sliceString(0, 10),
+          //   }
+          // });
+          // widgets.push(tr);
+
+          // let replaceDeco = Decoration.replace({
+          //   widget: new TextWidget(createPortal),
+          //   inclusive: false,
+          // }).range(node.from, node.to);
+          // widgets.push(replaceDeco);
+
           widgets.push(createWidget(state.doc.sliceString(node.from, node.to), node.from, node.to, createPortal).range(node.to));
         }
       }

From d25bbda72353b264af186ef44499c406cd69996d Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 12 Mar 2024 09:14:15 -0400
Subject: [PATCH 019/106] bounds are a bit off, but text changes

---
 src/editor/codemirror/CodeMirror.tsx          | 10 +-
 .../codemirror/reactWidgetExtension.tsx       | 94 +++++--------------
 2 files changed, 26 insertions(+), 78 deletions(-)

diff --git a/src/editor/codemirror/CodeMirror.tsx b/src/editor/codemirror/CodeMirror.tsx
index 495ed9ef8..aebee0185 100644
--- a/src/editor/codemirror/CodeMirror.tsx
+++ b/src/editor/codemirror/CodeMirror.tsx
@@ -143,12 +143,15 @@ const CodeMirror = ({
           logPastedLineCount(logging, update);
         }
       });
+      const view = new EditorView({
+        parent: elementRef.current!,
+      });
       const state = EditorState.create({
         doc: defaultValue,
         extensions: [
           notify,
           editorConfig,
-          reactWidgetExtension(portalFactory),
+          reactWidgetExtension(view, portalFactory),
           // Extension requires external state.
           dndSupport({ sessionSettings, setSessionSettings }),
           // Extensions only relevant for editing:
@@ -180,10 +183,7 @@ const CodeMirror = ({
           ]),
         ],
       });
-      const view = new EditorView({
-        state,
-        parent: elementRef.current!,
-      });
+      view.setState(state);
 
       viewRef.current = view;
       setActiveEditor(new EditorActions(view, logging, actionFeedback));
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 82b72d161..d2efbd27d 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -15,15 +15,22 @@ import { PortalFactory } from "./CodeMirror";
  * a proof of concept.
  */
 
-const ToggleReactComponent = ({ bval }: { bval: boolean }) => {
-  let x = bval ? "True" : "False"
+const ToggleReactComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
+  let curVal = view.state.doc.sliceString(from, to);
   const handleClick = useCallback(() => {
-    console.log();
-  }, []);
+    let opposite = curVal === "True" ? "False" : "True";
+    view.dispatch({
+      changes: {
+        from: from,
+        to: to,
+        insert: opposite,
+      }
+    });
+  }, [curVal, from, to, view]);
   return (
     <HStack fontFamily="body" spacing={5} py={3}>
       <Button onClick={handleClick}>Toggle</Button>
-      <Text fontWeight="semibold">Value: {x}</Text>
+      <Text fontWeight="semibold">Value: {curVal}</Text>
     </HStack>
   );
 };
@@ -35,14 +42,14 @@ const ToggleReactComponent = ({ bval }: { bval: boolean }) => {
 class ToggleWidget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private bval: boolean, private createPortal: PortalFactory) {
+  constructor(private from: number, private to: number, private createPortal: PortalFactory, private view: EditorView) {
     super();
   }
 
   toDOM() {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, < ToggleReactComponent bval={this.bval} />);
+    this.portalCleanup = this.createPortal(dom, < ToggleReactComponent from={this.from} to={this.to} view={this.view} />);
     return dom;
   }
 
@@ -57,45 +64,9 @@ class ToggleWidget extends WidgetType {
   }
 }
 
-
-const TextComponent = () => {
-  return (
-    <HStack fontFamily="body" spacing={5} py={3}>
-      <Text fontWeight="semibold">False</Text>
-    </HStack>
-  );
-};
-
-class TextWidget extends WidgetType {
-  private portalCleanup: (() => void) | undefined;
-
-  constructor(private createPortal: PortalFactory) {
-    super();
-  }
-
-  toDOM() {
-    const dom = document.createElement("div");
-
-    this.portalCleanup = this.createPortal(dom, < TextComponent />);
-    return dom;
-  }
-
-  destroy(dom: HTMLElement): void {
-    if (this.portalCleanup) {
-      this.portalCleanup();
-    }
-  }
-
-  ignoreEvent() {
-    return true;
-  }
-}
-
-function createWidget(bool: string, from: number, to: number, createPortal: PortalFactory): Decoration {
-  let bval = bool === "True"
-
+function createWidget(from: number, to: number, createPortal: PortalFactory, view: EditorView): Decoration {
   let deco = Decoration.widget({
-    widget: new ToggleWidget(bval, createPortal),
+    widget: new ToggleWidget(from, to, createPortal, view),
     side: 1,
   });
 
@@ -104,9 +75,11 @@ function createWidget(bool: string, from: number, to: number, createPortal: Port
 
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
+  view: EditorView,
   createPortal: PortalFactory
 ): Extension => {
-  const decorate = (state: EditorState) => {
+  const decorate = (state2: EditorState) => {
+    let state = view.state;
     let widgets: any[] = []
     let from = 0
     let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
@@ -115,37 +88,12 @@ export const reactWidgetExtension = (
 
     syntaxTree(state).iterate({
       from, to,
-      enter: (node: any) => { // TODO: type is SyntaxNode
-        if(node.name === "Boolean") {
-          // view.dispatch({
-          //   changes: {
-          //     from: node.from,
-          //     to: node.to,
-          //     insert: state.doc.sliceString(0, 10),
-          //   }
-          // });
-          // widgets.push(tr);
-
-          // let replaceDeco = Decoration.replace({
-          //   widget: new TextWidget(createPortal),
-          //   inclusive: false,
-          // }).range(node.from, node.to);
-          // widgets.push(replaceDeco);
-
-          widgets.push(createWidget(state.doc.sliceString(node.from, node.to), node.from, node.to, createPortal).range(node.to));
-        }
+      enter: (node: any) => { // TODO: type is SyntaxNode?
+        if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal, view).range(node.to));
       }
     })
 
     return Decoration.set(widgets)
-    
-    // const endOfFirstLine = state.doc.lineAt(0).to;
-    // const widget = Decoration.widget({
-    //   block: true,
-    //   widget: new ExampleReactBlockWidget(createPortal),
-    //   side: 1,
-    // });
-    // return Decoration.set(widget.range(endOfFirstLine));
   };
 
   const stateField = StateField.define<DecorationSet>({

From be632a8231910054db701d9e37b2444e9f478fd5 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 12 Mar 2024 09:44:56 -0400
Subject: [PATCH 020/106] view from DOM

---
 src/editor/codemirror/CodeMirror.tsx           | 11 ++++++-----
 src/editor/codemirror/reactWidgetExtension.tsx | 16 +++++++---------
 2 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/src/editor/codemirror/CodeMirror.tsx b/src/editor/codemirror/CodeMirror.tsx
index aebee0185..32c1d87d6 100644
--- a/src/editor/codemirror/CodeMirror.tsx
+++ b/src/editor/codemirror/CodeMirror.tsx
@@ -143,15 +143,13 @@ const CodeMirror = ({
           logPastedLineCount(logging, update);
         }
       });
-      const view = new EditorView({
-        parent: elementRef.current!,
-      });
+      
       const state = EditorState.create({
         doc: defaultValue,
         extensions: [
           notify,
           editorConfig,
-          reactWidgetExtension(view, portalFactory),
+          reactWidgetExtension(portalFactory),
           // Extension requires external state.
           dndSupport({ sessionSettings, setSessionSettings }),
           // Extensions only relevant for editing:
@@ -183,7 +181,10 @@ const CodeMirror = ({
           ]),
         ],
       });
-      view.setState(state);
+      const view = new EditorView({
+        state,
+        parent: elementRef.current!,
+      });
 
       viewRef.current = view;
       setActiveEditor(new EditorActions(view, logging, actionFeedback));
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index d2efbd27d..b08d4514c 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -42,14 +42,14 @@ const ToggleReactComponent = ({ from, to, view }: { from: number, to: number, vi
 class ToggleWidget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private from: number, private to: number, private createPortal: PortalFactory, private view: EditorView) {
+  constructor(private from: number, private to: number, private createPortal: PortalFactory) {
     super();
   }
 
-  toDOM() {
+  toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, < ToggleReactComponent from={this.from} to={this.to} view={this.view} />);
+    this.portalCleanup = this.createPortal(dom, < ToggleReactComponent from={this.from} to={this.to} view={view} />);
     return dom;
   }
 
@@ -64,9 +64,9 @@ class ToggleWidget extends WidgetType {
   }
 }
 
-function createWidget(from: number, to: number, createPortal: PortalFactory, view: EditorView): Decoration {
+function createWidget(from: number, to: number, createPortal: PortalFactory): Decoration {
   let deco = Decoration.widget({
-    widget: new ToggleWidget(from, to, createPortal, view),
+    widget: new ToggleWidget(from, to, createPortal),
     side: 1,
   });
 
@@ -75,11 +75,9 @@ function createWidget(from: number, to: number, createPortal: PortalFactory, vie
 
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
-  view: EditorView,
   createPortal: PortalFactory
 ): Extension => {
-  const decorate = (state2: EditorState) => {
-    let state = view.state;
+  const decorate = (state: EditorState) => {
     let widgets: any[] = []
     let from = 0
     let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
@@ -89,7 +87,7 @@ export const reactWidgetExtension = (
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => { // TODO: type is SyntaxNode?
-        if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal, view).range(node.to));
+        if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
       }
     })
 

From a85b3ce2c2c24a41c07ec65c00d89cf2038a6020 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 12 Mar 2024 14:54:12 +0100
Subject: [PATCH 021/106] .

---
 src/editor/codemirror/microbitWidget.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index e92d7ac4b..a127c368d 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -113,8 +113,8 @@ export const MicrobitComponent: React.FC = () => {
 
   const handleSubmit = () => {
     setIsVisible(false);
-    // Implement submit logic here
-    console.log("Submit button clicked");
+    // Implement logic here, change the arguments to the function
+    console.log("S");
   };
 
   return (

From ffdf5feb9aaacbddb9227a2a6ed0b3f8cf3a641d Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 12 Mar 2024 10:09:26 -0400
Subject: [PATCH 022/106] puts widget after set_pixel

---
 src/editor/codemirror/reactWidgetExtension.tsx | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 35332a9e0..a8a754ccf 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -84,11 +84,19 @@ export const reactWidgetExtension = (
     let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
     //let t = state.doc.toString()
     //console.log(t);
+    let setpix = false;
 
     syntaxTree(state).iterate({
       from, to,
       enter: (node: any) => { // TODO: type is SyntaxNode?
-        if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
+        //console.log();
+        //if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
+
+        // Found ArgList, will begin to parse nodes 
+        if(setpix && node.name === "ArgList") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
+          
+        // detected SoundEffect, if next expression is an ArgList, show UI
+        setpix = node.name === "PropertyName" && state.doc.sliceString(node.from, node.to) === "set_pixel"
       }
     })
 

From aeabe522d8113a877e44e7ae35bd15e985afa64b Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 12 Mar 2024 15:26:46 +0100
Subject: [PATCH 023/106] .

---
 src/editor/codemirror/microbitWidget.tsx      | 68 ++++++++++++-------
 .../codemirror/reactWidgetExtension.tsx       |  3 +-
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index a127c368d..6c2675d29 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -1,5 +1,11 @@
 import { Box, Grid, GridItem, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
 import { useState } from "react";
+import {
+    Decoration,
+    DecorationSet,
+    EditorView,
+    WidgetType,
+  } from "@codemirror/view";
 
 interface Pixel {
   x: number;
@@ -25,7 +31,7 @@ const MicrobitPixel: React.FC<{ brightness: number; selected: boolean; onClick:
 
 interface MicrobitGridProps {
   onClickPixel: (pixel: Pixel) => void;
-  onSubmit: () => void;
+  onSubmit: (x:number, y:number, brightness:number) => void;
   isVisible: boolean;
 }
 
@@ -48,7 +54,9 @@ const MicrobitGrid: React.FC<MicrobitGridProps> = ({ onClickPixel, onSubmit, isV
   };
 
   const handleOkClick = () => {
-    onSubmit();
+    if (selectedPixel) {
+      onSubmit(selectedPixel.x, selectedPixel.y, selectedPixel.brightness);
+    }
   };
 
   return (
@@ -103,30 +111,38 @@ const MicrobitGrid: React.FC<MicrobitGridProps> = ({ onClickPixel, onSubmit, isV
   );
 };
 
-export const MicrobitComponent: React.FC = () => {
-  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-  const [isVisible, setIsVisible] = useState(true);
-
-  const handleSelectPixel = (pixel: Pixel) => {
-    setSelectedPixel(pixel);
-  };
-
-  const handleSubmit = () => {
-    setIsVisible(false);
-    // Implement logic here, change the arguments to the function
-    console.log("S");
-  };
+export const MicrobitComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
+    const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+    const [isVisible, setIsVisible] = useState(true);
+  
+    const handleSelectPixel = (pixel: Pixel) => {
+      setSelectedPixel(pixel);
+    };
+  
+    const handleSubmit = (x: number, y: number, brightness: number) => {
+      setIsVisible(false);      
+      console.log(`Submitted pixel: (${x}, ${y}) - Brightness: ${brightness}`);
+      view.dispatch({
+          changes: {
+            from: from,
+            to: to,
+            insert: ("(${x}, ${y},${y}, ${brightness}) "),
+          }
+        });
+      };
+  
 
-  return (
-    <Box>
-      {isVisible && (
-        <MicrobitGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />
-      )}
-      {selectedPixel && isVisible && (
-        <Box mt="4px">
-          Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) - Brightness: {selectedPixel.brightness}
+    return (
+        <Box>
+          {isVisible && (
+            <MicrobitGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />
+          )}
+          {selectedPixel && isVisible && (
+            <Box mt="4px">
+              Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) - Brightness: {selectedPixel.brightness}
+            </Box>
+          )}
         </Box>
-      )}
-    </Box>
-  );
+    );
 };
+    
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 35332a9e0..549d053c7 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -50,7 +50,7 @@ class ToggleWidget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, <MicrobitComponent/>);
+    this.portalCleanup = this.createPortal(dom, <MicrobitComponent from={this.from} to={this.to} view={view} />);
     return dom;
   }
 
@@ -68,6 +68,7 @@ class ToggleWidget extends WidgetType {
 function createWidget(from: number, to: number, createPortal: PortalFactory): Decoration {
   let deco = Decoration.widget({
     widget: new ToggleWidget(from, to, createPortal),
+    //ToggleWidget(from, to, createPortal),
     side: 1,
   });
 

From f5c8a7b51d36e0f1d753dac2c7341e13a1242b18 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 12 Mar 2024 15:31:18 +0100
Subject: [PATCH 024/106] works yay

---
 src/editor/codemirror/microbitWidget.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index 6c2675d29..0cf9fc310 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -126,7 +126,7 @@ export const MicrobitComponent = ({ from, to, view }: { from: number, to: number
           changes: {
             from: from,
             to: to,
-            insert: ("(${x}, ${y},${y}, ${brightness}) "),
+            insert: (`(${x}, ${y}, ${brightness}) `),
           }
         });
       };

From 743714b3ebc245cf61d5e2e6c46754578bd84ad2 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 18 Mar 2024 17:03:51 +0100
Subject: [PATCH 025/106] added stuff

---
 src/editor/codemirror/microbitWidget.tsx      | 268 ++++++++++++------
 .../codemirror/reactWidgetExtension.tsx       |   5 +-
 2 files changed, 191 insertions(+), 82 deletions(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index 0cf9fc310..9588579ec 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -1,10 +1,7 @@
 import { Box, Grid, GridItem, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
 import { useState } from "react";
 import {
-    Decoration,
-    DecorationSet,
     EditorView,
-    WidgetType,
   } from "@codemirror/view";
 
 interface Pixel {
@@ -13,29 +10,13 @@ interface Pixel {
   brightness: number;
 }
 
-const MicrobitPixel: React.FC<{ brightness: number; selected: boolean; onClick: () => void }> = ({ brightness, selected, onClick }) => {
-  return (
-    <Button
-      size="sm"
-      h="20px"
-      w="20px"
-      p={0}
-      bgColor={selected ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(0, 0, 0, 1)"}
-      _hover={{ bgColor: selected ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(0, 0, 0, 1)" }}
-      onClick={onClick}
-      _focus={{ boxShadow: "none" }}
-      _active={{ bgColor: selected ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(0, 0, 0, 1)" }}
-    />
-  );
-};
-
-interface MicrobitGridProps {
+interface MicrobitSinglePixelGridProps {
   onClickPixel: (pixel: Pixel) => void;
   onSubmit: (x:number, y:number, brightness:number) => void;
   isVisible: boolean;
 }
 
-const MicrobitGrid: React.FC<MicrobitGridProps> = ({ onClickPixel, onSubmit, isVisible }) => {
+const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onClickPixel, onSubmit, isVisible }) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
   const [brightness, setBrightness] = useState<number>(5);
 
@@ -58,40 +39,59 @@ const MicrobitGrid: React.FC<MicrobitGridProps> = ({ onClickPixel, onSubmit, isV
       onSubmit(selectedPixel.x, selectedPixel.y, selectedPixel.brightness);
     }
   };
-
+  
   return (
-    <Box display={isVisible ? "flex" : "none"} flexDirection="row">
+  <>
+    {selectedPixel && (
+      <Box mt="4px">
+        <span style={{ fontSize: "small" }}>
+          Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) | Brightness: {selectedPixel.brightness}
+        </span>
+      </Box>
+    )}
+    <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
       <Box>
-        <Grid templateColumns={`repeat(5, 1fr)`} gap="2px" maxW="110px">
+        <Box bg="black" p="10px" borderRadius="5px">
           {[...Array(5)].map((_, x) => (
-            <GridItem key={x}>
-              <Grid templateColumns={`repeat(1, 1fr)`} gap="2px">
-                {[...Array(5)].map((_, y) => (
-                  <GridItem key={y}>
-                    <MicrobitPixel
-                      brightness={selectedPixel?.x === x && selectedPixel.y === y ? brightness : 0}
-                      selected={selectedPixel?.x === x && selectedPixel.y === y}
-                      onClick={() => handleClickPixel(x, y)}
-                    />
-                  </GridItem>
-                ))}
-              </Grid>
-            </GridItem>
+            <Box key={x} display="flex">
+              {[...Array(5)].map((_, y) => (
+                <Box key={y} display="flex" mr="2px">
+                  <Button
+                    size="xs"
+                    h="15px"
+                    w="15px"
+                    p={0}
+                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
+                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                    onClick={() => handleClickPixel(x, y)}
+                  />
+                </Box>
+              ))}
+            </Box>
           ))}
-        </Grid>
+        </Box>
+        {selectedPixel && (
+          <Box display="flex" flexDirection="column" alignItems="center" mt="10px">
+            <Box bg="white" borderRadius="5px" p="5px" textAlign="center">
+              <Button onClick={() => onSubmit(selectedPixel?.x ?? 0, selectedPixel?.y ?? 0, selectedPixel?.brightness ?? 0)} colorScheme="blue" size="sm">
+                Looks Good
+              </Button>
+            </Box>
+          </Box>
+        )}
       </Box>
       {selectedPixel && (
-        <Box ml="60px" mt="10px">
+        <Box ml="10px">
           <Slider
             aria-label="brightness"
             defaultValue={brightness}
-            min={1}
+            min={0}
             max={9}
             step={1}
-            onChange={handleSliderChange}
             orientation="vertical"
             _focus={{ boxShadow: "none" }}
             _active={{ bgColor: "transparent" }}
+            onChange={handleSliderChange}
           >
             <SliderTrack>
               <SliderFilledTrack />
@@ -100,49 +100,159 @@ const MicrobitGrid: React.FC<MicrobitGridProps> = ({ onClickPixel, onSubmit, isV
           </Slider>
         </Box>
       )}
-      {selectedPixel && (
-        <Box display="flex" justifyContent="center" ml="10px" mt="100px">
-          <Button onClick={handleOkClick} colorScheme="blue" size="sm">
-            Looks Good!
-          </Button>
-        </Box>
-      )}
     </Box>
+  </>
+);
+
+};
+
+export const MicrobitSinglePixelComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
+  const [isVisible, setIsVisible] = useState(true);
+  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+
+  const handleSelectPixel = (pixel: Pixel) => {
+    setSelectedPixel(pixel);
+  };
+
+  const handleSubmit = (x: number, y: number, brightness: number) => {
+    setIsVisible(false);      
+    view.dispatch({
+      changes: {
+        from: from,
+        to: to,
+        insert: (`(${x}, ${y}, ${brightness}) `),
+      }
+    });
+  };
+
+  return (
+        <MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />
   );
 };
 
-export const MicrobitComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
-    const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-    const [isVisible, setIsVisible] = useState(true);
-  
-    const handleSelectPixel = (pixel: Pixel) => {
-      setSelectedPixel(pixel);
-    };
-  
-    const handleSubmit = (x: number, y: number, brightness: number) => {
-      setIsVisible(false);      
-      console.log(`Submitted pixel: (${x}, ${y}) - Brightness: ${brightness}`);
-      view.dispatch({
-          changes: {
-            from: from,
-            to: to,
-            insert: (`(${x}, ${y}, ${brightness}) `),
-          }
-        });
-      };
-  
+interface MultiMicrobitGridProps {
+  selectedPixels: Pixel[];
+  onPixelClick: (x: number, y: number) => void;
+  onBrightnessChange: (x: number, y: number, brightness: number) => void;
+  onSubmit: () => void;
+  isVisible: boolean;
+}
 
-    return (
-        <Box>
-          {isVisible && (
-            <MicrobitGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />
-          )}
-          {selectedPixel && isVisible && (
-            <Box mt="4px">
-              Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) - Brightness: {selectedPixel.brightness}
+const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
+  selectedPixels,
+  onPixelClick,
+  onBrightnessChange,
+  onSubmit,
+  isVisible,
+}) => {
+  return (
+    <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
+      <Box>
+        <Box bg="black" p="10px" borderRadius="5px">
+          {[...Array(5)].map((_, x) => (
+            <Box key={x} display="flex">
+              {[...Array(5)].map((_, y) => (
+                <Box key={y} display="flex" mr="2px">
+                  <Button
+                    size="xs"
+                    h="15px"
+                    w="15px"
+                    p={0}
+                    bgColor={selectedPixels.some(p => p.x === x && p.y === y) ? `rgba(255, 0, 0, ${(selectedPixels.find(p => p.x === x && p.y === y)!.brightness) / 9})` : "rgba(255, 255, 255, 0)"}
+                    _hover={{ bgColor: selectedPixels.some(p => p.x === x && p.y === y) ? `rgba(255, 0, 0, ${(selectedPixels.find(p => p.x === x && p.y === y)!.brightness) / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                    onClick={() => onPixelClick(x, y)}
+                  />
+                </Box>
+              ))}
             </Box>
-          )}
+          ))}
         </Box>
-    );
+        <Box display="flex" justifyContent="center" mt="10px">
+          <Box bg="white" borderRadius="5px" p="5px">
+            <Button onClick={onSubmit} colorScheme="blue" size="sm">
+              Looks Good
+            </Button>
+          </Box>
+        </Box>
+      </Box>
+      <Box ml="10px">
+        <Slider
+          aria-label="brightness"
+          defaultValue={5}
+          min={0}
+          max={9}
+          step={1}
+          orientation="vertical"
+          _focus={{ boxShadow: "none" }}
+          _active={{ bgColor: "transparent" }}
+          onChange={(value) => onBrightnessChange(selectedPixels[selectedPixels.length - 1].x, selectedPixels[selectedPixels.length - 1].y, value)}
+        >
+          <SliderTrack>
+            <SliderFilledTrack />
+          </SliderTrack>
+          <SliderThumb />
+        </Slider>
+      </Box>
+    </Box>
+  );
 };
-    
+
+export const MicrobitMultiplePixelComponent = ({
+  from,
+  to,
+  view,
+}: {
+  from: number;
+  to: number;
+  view: EditorView;
+}) => {
+  const initialSelectedPixels: Pixel[] = [];
+  
+  for (let x = from; x <= to; x++) {
+    for (let y = from; y <= to; y++) {
+      initialSelectedPixels.push({ x, y, brightness: 0 });
+    }
+  }
+
+  const [selectedPixels, setSelectedPixels] = useState<Pixel[]>(initialSelectedPixels);
+  const [isVisible, setIsVisible] = useState(true);
+
+  const handlePixelClick = (x: number, y: number) => {
+    const existingIndex = selectedPixels.findIndex(pixel => pixel.x === x && pixel.y === y);
+    const brightness = selectedPixels[selectedPixels.length - 1]?.brightness ?? 5;
+
+    if (existingIndex !== -1) {
+      const updatedPixels = [...selectedPixels];
+      updatedPixels[existingIndex].brightness = brightness;
+      setSelectedPixels(updatedPixels);
+    } else {
+      const newPixel: Pixel = { x, y, brightness };
+      setSelectedPixels([...selectedPixels, newPixel]);
+    }
+  };
+
+  const handleSubmit = () => {
+    setIsVisible(false);
+  };
+
+  const handleBrightnessChange = (x: number, y: number, brightness: number) => {
+    setSelectedPixels(prevPixels => {
+      const updatedPixels = [...prevPixels];
+      const pixelIndex = updatedPixels.findIndex(pixel => pixel.x === x && pixel.y === y);
+      if (pixelIndex !== -1) {
+        updatedPixels[pixelIndex] = { x, y, brightness };
+      }
+      return updatedPixels;
+    });
+  };
+
+  return (
+    <MicrobitMultiplePixelsGrid
+      selectedPixels={selectedPixels}
+      onPixelClick={handlePixelClick}
+      onBrightnessChange={handleBrightnessChange}
+      onSubmit={handleSubmit}
+      isVisible={isVisible}
+    />
+  );
+};
\ No newline at end of file
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index ef26e83bd..f6bd2040f 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -9,8 +9,7 @@ import {
 import { syntaxTree } from "@codemirror/language"
 import { useState, useCallback } from "react";
 import { PortalFactory } from "./CodeMirror";
-import {MicrobitComponent} from "./microbitWidget";
-
+import {MicrobitSinglePixelComponent, MicrobitMultiplePixelComponent} from "./microbitWidget";
 /**
  * An example react component that we use inside a CodeMirror widget as
  * a proof of concept.
@@ -50,7 +49,7 @@ class ToggleWidget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, <MicrobitComponent from={this.from} to={this.to} view={view} />);
+    this.portalCleanup = this.createPortal(dom, <MicrobitMultiplePixelComponent from={this.from} to={this.to} view={view} />);
     return dom;
   }
 

From 63555dfd130b1eb4ca1c4b9a0859fc3527ec8605 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 18 Mar 2024 21:35:02 +0100
Subject: [PATCH 026/106] kinda works i think t's good enough as a prototype
 not gonna do much more

---
 src/editor/codemirror/microbitWidget.tsx | 173 +++++++++++------------
 1 file changed, 82 insertions(+), 91 deletions(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index 9588579ec..70e89c613 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -12,7 +12,7 @@ interface Pixel {
 
 interface MicrobitSinglePixelGridProps {
   onClickPixel: (pixel: Pixel) => void;
-  onSubmit: (x:number, y:number, brightness:number) => void;
+  onSubmit: () => void;
   isVisible: boolean;
 }
 
@@ -33,77 +33,70 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
       onClickPixel(updatedPixel);
     }
   };
-
-  const handleOkClick = () => {
-    if (selectedPixel) {
-      onSubmit(selectedPixel.x, selectedPixel.y, selectedPixel.brightness);
-    }
-  };
   
   return (
-  <>
-    {selectedPixel && (
-      <Box mt="4px">
-        <span style={{ fontSize: "small" }}>
-          Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) | Brightness: {selectedPixel.brightness}
-        </span>
-      </Box>
-    )}
-    <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
-      <Box>
-        <Box bg="black" p="10px" borderRadius="5px">
-          {[...Array(5)].map((_, x) => (
-            <Box key={x} display="flex">
-              {[...Array(5)].map((_, y) => (
-                <Box key={y} display="flex" mr="2px">
-                  <Button
-                    size="xs"
-                    h="15px"
-                    w="15px"
-                    p={0}
-                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
-                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
-                    onClick={() => handleClickPixel(x, y)}
-                  />
-                </Box>
-              ))}
+    <>
+      <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
+        <Box>
+          <Box bg="black" p="10px" borderRadius="5px">
+            {[...Array(5)].map((_, x) => (
+              <Box key={x} display="flex">
+                {[...Array(5)].map((_, y) => (
+                  <Box key={y} display="flex" mr="2px">
+                    <Button
+                      size="xs"
+                      h="15px"
+                      w="15px"
+                      p={0}
+                      bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
+                      _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                      onClick={() => handleClickPixel(x, y)}
+                    />
+                  </Box>
+                ))}
+              </Box>
+            ))}
+          </Box>
+          {selectedPixel && (
+            <Box display="flex" flexDirection="column" alignItems="center" mt="10px">
+              <Box bg="white" borderRadius="5px" p="5px" textAlign="center">
+                <Button onClick={() => onSubmit()} colorScheme="blue" size="sm">
+                  Looks Good
+                </Button>
+              </Box>
             </Box>
-          ))}
+          )}
         </Box>
         {selectedPixel && (
-          <Box display="flex" flexDirection="column" alignItems="center" mt="10px">
-            <Box bg="white" borderRadius="5px" p="5px" textAlign="center">
-              <Button onClick={() => onSubmit(selectedPixel?.x ?? 0, selectedPixel?.y ?? 0, selectedPixel?.brightness ?? 0)} colorScheme="blue" size="sm">
-                Looks Good
-              </Button>
-            </Box>
+          <Box ml="10px">
+            <Slider
+              aria-label="brightness"
+              defaultValue={brightness}
+              min={0}
+              max={9}
+              step={1}
+              orientation="vertical"
+              _focus={{ boxShadow: "none" }}
+              _active={{ bgColor: "transparent" }}
+              onChange={handleSliderChange}
+            >
+              <SliderTrack>
+                <SliderFilledTrack />
+              </SliderTrack>
+              <SliderThumb />
+            </Slider>
           </Box>
         )}
       </Box>
       {selectedPixel && (
-        <Box ml="10px">
-          <Slider
-            aria-label="brightness"
-            defaultValue={brightness}
-            min={0}
-            max={9}
-            step={1}
-            orientation="vertical"
-            _focus={{ boxShadow: "none" }}
-            _active={{ bgColor: "transparent" }}
-            onChange={handleSliderChange}
-          >
-            <SliderTrack>
-              <SliderFilledTrack />
-            </SliderTrack>
-            <SliderThumb />
-          </Slider>
+        <Box mt="4px">
+          <span style={{ fontSize: "small" }}>
+            Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) | Brightness: {selectedPixel.brightness}
+          </span>
         </Box>
       )}
-    </Box>
-  </>
-);
-
+    </>
+  );
 };
 
 export const MicrobitSinglePixelComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
@@ -114,20 +107,21 @@ export const MicrobitSinglePixelComponent = ({ from, to, view }: { from: number,
     setSelectedPixel(pixel);
   };
 
-  const handleSubmit = (x: number, y: number, brightness: number) => {
-    setIsVisible(false);      
-    view.dispatch({
-      changes: {
-        from: from,
-        to: to,
-        insert: (`(${x}, ${y}, ${brightness}) `),
-      }
-    });
+  const handleSubmit = () => {
+    if (selectedPixel !== null) {
+      const { x, y, brightness } = selectedPixel;
+      setIsVisible(false);      
+      view.dispatch({
+        changes: {
+          from: from,
+          to: to,
+          insert: `(${x}, ${y}, ${brightness}) `,
+        }
+      });
+    }
   };
 
-  return (
-        <MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />
-  );
+  return (<MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />);
 };
 
 interface MultiMicrobitGridProps {
@@ -136,6 +130,7 @@ interface MultiMicrobitGridProps {
   onBrightnessChange: (x: number, y: number, brightness: number) => void;
   onSubmit: () => void;
   isVisible: boolean;
+  currentBrightness : number;
 }
 
 const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
@@ -144,6 +139,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   onBrightnessChange,
   onSubmit,
   isVisible,
+  currentBrightness
 }) => {
   return (
     <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
@@ -178,7 +174,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
       <Box ml="10px">
         <Slider
           aria-label="brightness"
-          defaultValue={5}
+          value={currentBrightness}
           min={0}
           max={9}
           step={1}
@@ -197,50 +193,44 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent = ({
-  from,
-  to,
-  view,
-}: {
-  from: number;
-  to: number;
-  view: EditorView;
-}) => {
+export const MicrobitMultiplePixelComponent = ({ from, to, view }: { from: number; to: number; view: EditorView; }) => {
   const initialSelectedPixels: Pixel[] = [];
-  
-  for (let x = from; x <= to; x++) {
-    for (let y = from; y <= to; y++) {
+  /*
+  for (let x = 0; x <= 4; x++) {
+    for (let y = 0; y <= 4; y++) {
       initialSelectedPixels.push({ x, y, brightness: 0 });
     }
   }
+  */
 
   const [selectedPixels, setSelectedPixels] = useState<Pixel[]>(initialSelectedPixels);
   const [isVisible, setIsVisible] = useState(true);
+  const [currentBrightness, setCurrentBrightness] = useState(5);
 
   const handlePixelClick = (x: number, y: number) => {
     const existingIndex = selectedPixels.findIndex(pixel => pixel.x === x && pixel.y === y);
-    const brightness = selectedPixels[selectedPixels.length - 1]?.brightness ?? 5;
-
     if (existingIndex !== -1) {
       const updatedPixels = [...selectedPixels];
-      updatedPixels[existingIndex].brightness = brightness;
+      updatedPixels[existingIndex].brightness = currentBrightness;
       setSelectedPixels(updatedPixels);
     } else {
-      const newPixel: Pixel = { x, y, brightness };
+      const newPixel: Pixel = { x, y, brightness: currentBrightness };
       setSelectedPixels([...selectedPixels, newPixel]);
     }
   };
 
   const handleSubmit = () => {
+    //add the logic to change the arguments to the function 
     setIsVisible(false);
   };
 
   const handleBrightnessChange = (x: number, y: number, brightness: number) => {
+    setCurrentBrightness(brightness);
     setSelectedPixels(prevPixels => {
       const updatedPixels = [...prevPixels];
       const pixelIndex = updatedPixels.findIndex(pixel => pixel.x === x && pixel.y === y);
       if (pixelIndex !== -1) {
-        updatedPixels[pixelIndex] = { x, y, brightness };
+        updatedPixels[pixelIndex].brightness = brightness;
       }
       return updatedPixels;
     });
@@ -253,6 +243,7 @@ export const MicrobitMultiplePixelComponent = ({
       onBrightnessChange={handleBrightnessChange}
       onSubmit={handleSubmit}
       isVisible={isVisible}
+      currentBrightness={currentBrightness}
     />
   );
 };
\ No newline at end of file

From 5ebb7b8a3efc2a33668b1ebd073cfaf039e1a50d Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 18 Mar 2024 21:35:57 +0100
Subject: [PATCH 027/106] comment

---
 src/editor/codemirror/microbitWidget.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index 70e89c613..263c87214 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -196,6 +196,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
 export const MicrobitMultiplePixelComponent = ({ from, to, view }: { from: number; to: number; view: EditorView; }) => {
   const initialSelectedPixels: Pixel[] = [];
   /*
+  //Probably unnecessary to intialize the state, we can set it to 0 in the arguments by default anyway and it messes up some other logic
   for (let x = 0; x <= 4; x++) {
     for (let y = 0; y <= 4; y++) {
       initialSelectedPixels.push({ x, y, brightness: 0 });

From 287de483a3df289168583a55b6324ceb5656ddb2 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 18 Mar 2024 22:03:00 +0100
Subject: [PATCH 028/106] lint

---
 src/editor/codemirror/microbitWidget.tsx       | 2 +-
 src/editor/codemirror/reactWidgetExtension.tsx | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index 263c87214..0b8d9f073 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -1,4 +1,4 @@
-import { Box, Grid, GridItem, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
+import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
 import { useState } from "react";
 import {
     EditorView,
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index f6bd2040f..0810b62f3 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -7,9 +7,9 @@ import {
   WidgetType,
 } from "@codemirror/view";
 import { syntaxTree } from "@codemirror/language"
-import { useState, useCallback } from "react";
+import { useCallback } from "react";
 import { PortalFactory } from "./CodeMirror";
-import {MicrobitSinglePixelComponent, MicrobitMultiplePixelComponent} from "./microbitWidget";
+import { MicrobitMultiplePixelComponent} from "./microbitWidget";
 /**
  * An example react component that we use inside a CodeMirror widget as
  * a proof of concept.

From 161f92eaa9e6081cb4dcf44bbf00acf7ea9629f6 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 18 Mar 2024 22:05:06 +0100
Subject: [PATCH 029/106] to show the single on cloudeflare

---
 src/editor/codemirror/reactWidgetExtension.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 0810b62f3..45690f983 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -9,7 +9,7 @@ import {
 import { syntaxTree } from "@codemirror/language"
 import { useCallback } from "react";
 import { PortalFactory } from "./CodeMirror";
-import { MicrobitMultiplePixelComponent} from "./microbitWidget";
+import {MicrobitSinglePixelComponent, MicrobitMultiplePixelComponent} from "./microbitWidget";
 /**
  * An example react component that we use inside a CodeMirror widget as
  * a proof of concept.

From e12ab3aacaaf2e4bb4f3455bca13e7ac3359ed44 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 19 Mar 2024 07:29:15 +0100
Subject: [PATCH 030/106] ye

---
 src/editor/codemirror/microbitWidget.tsx | 45 ++++++++++++++++--------
 1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/microbitWidget.tsx
index 0b8d9f073..920186497 100644
--- a/src/editor/codemirror/microbitWidget.tsx
+++ b/src/editor/codemirror/microbitWidget.tsx
@@ -39,10 +39,10 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
       <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
         <Box>
           <Box bg="black" p="10px" borderRadius="5px">
-            {[...Array(5)].map((_, x) => (
-              <Box key={x} display="flex">
-                {[...Array(5)].map((_, y) => (
-                  <Box key={y} display="flex" mr="2px">
+            {[...Array(5)].map((_, y) => (
+              <Box key={y} display="flex">
+                {[...Array(5)].map((_, x) => (
+                  <Box key={x} display="flex" mr="2px">
                     <Button
                       size="xs"
                       h="15px"
@@ -66,8 +66,16 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
               </Box>
             </Box>
           )}
+          {!selectedPixel && (
+            <Box display="flex" flexDirection="column" alignItems="center" mt="10px">
+              <Box bg="white" borderRadius="5px" p="5px" textAlign="center">
+                <Button disabled colorScheme="blue" size="sm">
+                  Looks Good
+                </Button>
+              </Box>
+            </Box>
+          )}
         </Box>
-        {selectedPixel && (
           <Box ml="10px">
             <Slider
               aria-label="brightness"
@@ -78,20 +86,25 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
               orientation="vertical"
               _focus={{ boxShadow: "none" }}
               _active={{ bgColor: "transparent" }}
-              onChange={handleSliderChange}
-            >
+              onChange={handleSliderChange}>
               <SliderTrack>
                 <SliderFilledTrack />
               </SliderTrack>
               <SliderThumb />
             </Slider>
           </Box>
-        )}
       </Box>
       {selectedPixel && (
         <Box mt="4px">
           <span style={{ fontSize: "small" }}>
-            Selected Pixel: ({selectedPixel.x}, {selectedPixel.y}) | Brightness: {selectedPixel.brightness}
+            Selected pixel: ({selectedPixel.x}, {selectedPixel.y}) | Brightness: {selectedPixel.brightness}
+          </span>
+        </Box>
+      )}
+      {!selectedPixel && (
+        <Box mt="4px">
+          <span style={{ fontSize: "small" }}>
+          Select a pixel
           </span>
         </Box>
       )}
@@ -145,10 +158,10 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
     <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
       <Box>
         <Box bg="black" p="10px" borderRadius="5px">
-          {[...Array(5)].map((_, x) => (
-            <Box key={x} display="flex">
-              {[...Array(5)].map((_, y) => (
-                <Box key={y} display="flex" mr="2px">
+          {[...Array(5)].map((_, y) => (
+            <Box key={y} display="flex">
+              {[...Array(5)].map((_, x) => (
+                <Box key={x} display="flex" mr="2px">
                   <Button
                     size="xs"
                     h="15px"
@@ -181,8 +194,10 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
           orientation="vertical"
           _focus={{ boxShadow: "none" }}
           _active={{ bgColor: "transparent" }}
-          onChange={(value) => onBrightnessChange(selectedPixels[selectedPixels.length - 1].x, selectedPixels[selectedPixels.length - 1].y, value)}
-        >
+          onChange={(value) => {
+            const lastPixel = selectedPixels.length > 0 ? selectedPixels[selectedPixels.length - 1] : { x: -1, y: -1 };
+            onBrightnessChange(lastPixel.x, lastPixel.y, value);
+          }}        >
           <SliderTrack>
             <SliderFilledTrack />
           </SliderTrack>

From d1f4e7171f19ee7918e5e870ffe1804609ebc343 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 19 Mar 2024 12:24:52 +0100
Subject: [PATCH 031/106] .

---
 .../codemirror/reactWidgetExtension.tsx       | 30 ++-----------------
 1 file changed, 3 insertions(+), 27 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 45690f983..669fb0e14 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -7,33 +7,9 @@ import {
   WidgetType,
 } from "@codemirror/view";
 import { syntaxTree } from "@codemirror/language"
-import { useCallback } from "react";
 import { PortalFactory } from "./CodeMirror";
-import {MicrobitSinglePixelComponent, MicrobitMultiplePixelComponent} from "./microbitWidget";
-/**
- * An example react component that we use inside a CodeMirror widget as
- * a proof of concept.
- */
-
-const ToggleReactComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
-  let curVal = view.state.doc.sliceString(from, to);
-  const handleClick = useCallback(() => {
-    let opposite = curVal === "True" ? "False" : "True";
-    view.dispatch({
-      changes: {
-        from: from,
-        to: to,
-        insert: opposite,
-      }
-    });
-  }, [curVal, from, to, view]);
-  return (
-    <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleClick}>Toggle</Button>
-      <Text fontWeight="semibold">Value: {curVal}</Text>
-    </HStack>
-  );
-};
+import {MicrobitSinglePixelComponent} from "./microbitWidget";
+//MicrobitMultiplePixelComponent
 
 /**
  * This widget will have its contents rendered by the code in CodeMirror.tsx
@@ -49,7 +25,7 @@ class ToggleWidget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, <MicrobitMultiplePixelComponent from={this.from} to={this.to} view={view} />);
+    this.portalCleanup = this.createPortal(dom, <MicrobitSinglePixelComponent from={this.from} to={this.to} view={view} />);
     return dom;
   }
 

From 67e51f85e23600b28a1b076de43e6c30d867b74a Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 19 Mar 2024 12:41:36 +0100
Subject: [PATCH 032/106] .

---
 src/editor/codemirror/reactWidgetExtension.tsx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 669fb0e14..ca01ac3bb 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -1,4 +1,3 @@
-import { Button, HStack, Text } from "@chakra-ui/react";
 import { EditorState, Extension, StateField } from "@codemirror/state";
 import {
   Decoration,

From e590d039694e8f754d0ef45297d546fb7d557584 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 19 Mar 2024 15:05:10 +0100
Subject: [PATCH 033/106] .

---
 src/editor/codemirror/reactWidgetExtension.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index ca01ac3bb..30e1147a6 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -7,7 +7,7 @@ import {
 } from "@codemirror/view";
 import { syntaxTree } from "@codemirror/language"
 import { PortalFactory } from "./CodeMirror";
-import {MicrobitSinglePixelComponent} from "./microbitWidget";
+import {MicrobitMultiplePixelComponent} from "./microbitWidget";
 //MicrobitMultiplePixelComponent
 
 /**
@@ -24,7 +24,7 @@ class ToggleWidget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, <MicrobitSinglePixelComponent from={this.from} to={this.to} view={view} />);
+    this.portalCleanup = this.createPortal(dom, <MicrobitMultiplePixelComponent from={this.from} to={this.to} view={view} />);
     return dom;
   }
 

From 19b583009f2b63ee8438bf042ba7fd7d394ac428 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 19 Mar 2024 15:53:05 +0100
Subject: [PATCH 034/106] interfac

---
 src/editor/codemirror/reactWidgetExtension.tsx | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 30e1147a6..815acac18 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -49,6 +49,14 @@ function createWidget(from: number, to: number, createPortal: PortalFactory): De
   return deco;
 }
 
+
+interface WidgetProps{
+  from : number,
+  to : number,
+  view : EditorView,
+  arguments : any
+}
+
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory

From 6aade46352b9e3cca9608662faff5a9b34d64d93 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 19 Mar 2024 16:05:21 +0100
Subject: [PATCH 035/106] .

---
 src/editor/codemirror/reactWidgetExtension.tsx | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index 815acac18..d1923761e 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -8,6 +8,7 @@ import {
 import { syntaxTree } from "@codemirror/language"
 import { PortalFactory } from "./CodeMirror";
 import {MicrobitMultiplePixelComponent} from "./microbitWidget";
+import React from "react";
 //MicrobitMultiplePixelComponent
 
 /**
@@ -17,14 +18,13 @@ import {MicrobitMultiplePixelComponent} from "./microbitWidget";
 class ToggleWidget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private from: number, private to: number, private createPortal: PortalFactory) {
+  constructor(private from: number, private to: number, private createPortal: PortalFactory, private component : React.ComponentType<any>) {
     super();
   }
 
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
-
-    this.portalCleanup = this.createPortal(dom, <MicrobitMultiplePixelComponent from={this.from} to={this.to} view={view} />);
+    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { from: this.from, to: this.to, view: view }));
     return dom;
   }
 
@@ -41,7 +41,7 @@ class ToggleWidget extends WidgetType {
 
 function createWidget(from: number, to: number, createPortal: PortalFactory): Decoration {
   let deco = Decoration.widget({
-    widget: new ToggleWidget(from, to, createPortal),
+    widget: new ToggleWidget(from, to, createPortal, MicrobitMultiplePixelComponent),
     //ToggleWidget(from, to, createPortal),
     side: 1,
   });
@@ -49,12 +49,11 @@ function createWidget(from: number, to: number, createPortal: PortalFactory): De
   return deco;
 }
 
-
 interface WidgetProps{
   from : number,
   to : number,
   view : EditorView,
-  arguments : any
+  arguments : any 
 }
 
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there

From b58ec2c8a1b02dc86fcef62d6bc3ddce4a2ea684 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Wed, 20 Mar 2024 11:56:38 -0400
Subject: [PATCH 036/106] better property passing to widgets

---
 .../codemirror/reactWidgetExtension.tsx       | 41 ++++++++++++-------
 1 file changed, 26 insertions(+), 15 deletions(-)

diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/reactWidgetExtension.tsx
index d1923761e..c4760686e 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/reactWidgetExtension.tsx
@@ -11,20 +11,26 @@ import {MicrobitMultiplePixelComponent} from "./microbitWidget";
 import React from "react";
 //MicrobitMultiplePixelComponent
 
+interface WidgetProps<T>{
+  from : number,
+  to : number,
+  arguments : T[]
+}
+
 /**
  * This widget will have its contents rendered by the code in CodeMirror.tsx
  * which it communicates with via the portal factory.
  */
-class ToggleWidget extends WidgetType {
+class Widget<T> extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private from: number, private to: number, private createPortal: PortalFactory, private component : React.ComponentType<any>) {
+  constructor(private component : React.ComponentType<any>, private props: WidgetProps<T>, private createPortal: PortalFactory, ) {
     super();
   }
 
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
-    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { from: this.from, to: this.to, view: view }));
+    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { props: this.props, view: view }));
     return dom;
   }
 
@@ -39,23 +45,20 @@ class ToggleWidget extends WidgetType {
   }
 }
 
-function createWidget(from: number, to: number, createPortal: PortalFactory): Decoration {
+function createWidget<T>(comp: React.ComponentType<any>, from: number, to: number, args: T[], createPortal: PortalFactory): Decoration {
+  let props = {
+    from : from,
+    to : to,
+    arguments : args
+  }
   let deco = Decoration.widget({
-    widget: new ToggleWidget(from, to, createPortal, MicrobitMultiplePixelComponent),
-    //ToggleWidget(from, to, createPortal),
+    widget: new Widget(comp, props, createPortal),
     side: 1,
   });
 
   return deco;
 }
 
-interface WidgetProps{
-  from : number,
-  to : number,
-  view : EditorView,
-  arguments : any 
-}
-
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
@@ -75,10 +78,18 @@ export const reactWidgetExtension = (
         //if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
 
         // Found ArgList, will begin to parse nodes 
-        if(setpix && node.name === "ArgList") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
-          
+        if(setpix && node.name === "ArgList") {
+          widgets.push(createWidget<number>(
+            MicrobitMultiplePixelComponent, 
+            node.from, node.to, 
+            [1, 2, 3],
+            createPortal).range(node.to));
+        }
         // detected SoundEffect, if next expression is an ArgList, show UI
         setpix = node.name === "PropertyName" && state.doc.sliceString(node.from, node.to) === "set_pixel"
+        if(setpix) {
+          
+        }
       }
     })
 

From 64a876b7bad7363fb371b822c62bddf2ad76ad20 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Wed, 20 Mar 2024 12:16:35 -0400
Subject: [PATCH 037/106] organized into subdir helper-widgets

---
 src/editor/codemirror/CodeMirror.tsx          |  2 +-
 .../helper-widgets/argumentParser.tsx         | 10 +++++
 .../{ => helper-widgets}/microbitWidget.tsx   |  0
 .../reactWidgetExtension.tsx                  | 42 +++++++++++--------
 4 files changed, 36 insertions(+), 18 deletions(-)
 create mode 100644 src/editor/codemirror/helper-widgets/argumentParser.tsx
 rename src/editor/codemirror/{ => helper-widgets}/microbitWidget.tsx (100%)
 rename src/editor/codemirror/{ => helper-widgets}/reactWidgetExtension.tsx (69%)

diff --git a/src/editor/codemirror/CodeMirror.tsx b/src/editor/codemirror/CodeMirror.tsx
index 32c1d87d6..7173852e7 100644
--- a/src/editor/codemirror/CodeMirror.tsx
+++ b/src/editor/codemirror/CodeMirror.tsx
@@ -48,7 +48,7 @@ import { languageServer } from "./language-server/view";
 import { lintGutter } from "./lint/lint";
 import { codeStructure } from "./structure-highlighting";
 import themeExtensions from "./themeExtensions";
-import { reactWidgetExtension } from "./reactWidgetExtension";
+import { reactWidgetExtension } from "./helper-widgets/reactWidgetExtension";
 
 interface CodeMirrorProps {
   className?: string;
diff --git a/src/editor/codemirror/helper-widgets/argumentParser.tsx b/src/editor/codemirror/helper-widgets/argumentParser.tsx
new file mode 100644
index 000000000..f5cba3157
--- /dev/null
+++ b/src/editor/codemirror/helper-widgets/argumentParser.tsx
@@ -0,0 +1,10 @@
+import { EditorState } from "@codemirror/state";
+
+// Pre: args are all of type number
+export function numberArgs(state: EditorState, args: any[]): number[] {
+    let nums = []
+    args.forEach(function (value) {
+        
+    }); 
+    return []
+}
\ No newline at end of file
diff --git a/src/editor/codemirror/microbitWidget.tsx b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
similarity index 100%
rename from src/editor/codemirror/microbitWidget.tsx
rename to src/editor/codemirror/helper-widgets/microbitWidget.tsx
diff --git a/src/editor/codemirror/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
similarity index 69%
rename from src/editor/codemirror/reactWidgetExtension.tsx
rename to src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index c4760686e..eb98d11e1 100644
--- a/src/editor/codemirror/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -6,10 +6,10 @@ import {
   WidgetType,
 } from "@codemirror/view";
 import { syntaxTree } from "@codemirror/language"
-import { PortalFactory } from "./CodeMirror";
-import {MicrobitMultiplePixelComponent} from "./microbitWidget";
+import { PortalFactory } from "../CodeMirror";
 import React from "react";
-//MicrobitMultiplePixelComponent
+import {MicrobitMultiplePixelComponent, MicrobitSinglePixelComponent} from "./microbitWidget";
+import { numberArgs } from "./argumentParser";
 
 interface WidgetProps<T>{
   from : number,
@@ -70,26 +70,34 @@ export const reactWidgetExtension = (
     //let t = state.doc.toString()
     //console.log(t);
     let setpix = false;
-
+    let image = false;
     syntaxTree(state).iterate({
       from, to,
-      enter: (node: any) => { // TODO: type is SyntaxNode?
-        //console.log();
-        //if(node.name === "Boolean") widgets.push(createWidget(node.from, node.to, createPortal).range(node.to));
+      enter: (ref) => {
+        //console.log(ref.name);
 
         // Found ArgList, will begin to parse nodes 
-        if(setpix && node.name === "ArgList") {
-          widgets.push(createWidget<number>(
-            MicrobitMultiplePixelComponent, 
-            node.from, node.to, 
-            [1, 2, 3],
-            createPortal).range(node.to));
-        }
-        // detected SoundEffect, if next expression is an ArgList, show UI
-        setpix = node.name === "PropertyName" && state.doc.sliceString(node.from, node.to) === "set_pixel"
-        if(setpix) {
+        if(setpix && ref.name === "ArgList") {
+          let cs = ref.node.getChildren("Number");
+          if(cs.length === 3) {
+            widgets.push(createWidget<number>(
+              MicrobitSinglePixelComponent, 
+              ref.from, ref.to, 
+              numberArgs(state, cs),
+              createPortal).range(ref.to));
+            }
+          }
+        if(image && ref.name === "ArgList"){
+          let s = ref.node.getChild("ContinuedString");
           
         }
+
+        // detected set_pixel, if next expression is an ArgList, show UI
+        setpix = ref.name === "PropertyName" && state.doc.sliceString(ref.from, ref.to) === "set_pixel"
+        if(setpix){
+          console.log(ref.node.nextSibling);
+        }
+        image = ref.name === "VariableName" && state.doc.sliceString(ref.from, ref.to) === "Image"
       }
     })
 

From 31fddfca119c6c813e4ce2710b304d2eef313126 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Fri, 22 Mar 2024 22:44:28 -0400
Subject: [PATCH 038/106] better argument passing and syntax tree detection

---
 .../helper-widgets/argumentParser.tsx         |  3 +
 .../helper-widgets/reactWidgetExtension.tsx   | 90 ++++++++++---------
 2 files changed, 50 insertions(+), 43 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/argumentParser.tsx b/src/editor/codemirror/helper-widgets/argumentParser.tsx
index f5cba3157..fa5bf1e42 100644
--- a/src/editor/codemirror/helper-widgets/argumentParser.tsx
+++ b/src/editor/codemirror/helper-widgets/argumentParser.tsx
@@ -1,5 +1,8 @@
 import { EditorState } from "@codemirror/state";
 
+// TODO: might move parsing to here once arguments are no longer literals
+// see index.d.ts
+
 // Pre: args are all of type number
 export function numberArgs(state: EditorState, args: any[]): number[] {
     let nums = []
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index eb98d11e1..e50928a3c 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -12,9 +12,11 @@ import {MicrobitMultiplePixelComponent, MicrobitSinglePixelComponent} from "./mi
 import { numberArgs } from "./argumentParser";
 
 interface WidgetProps<T>{
+  // Where to insert the changed values
   from : number,
   to : number,
-  arguments : T[]
+  // Note: always an array, can be singleton
+  arguments : T[] 
 }
 
 /**
@@ -45,59 +47,61 @@ class Widget<T> extends WidgetType {
   }
 }
 
-function createWidget<T>(comp: React.ComponentType<any>, from: number, to: number, args: T[], createPortal: PortalFactory): Decoration {
-  let props = {
-    from : from,
-    to : to,
-    arguments : args
-  }
-  let deco = Decoration.widget({
-    widget: new Widget(comp, props, createPortal),
-    side: 1,
-  });
-
-  return deco;
-}
-
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
 ): Extension => {
   const decorate = (state: EditorState) => {
     let widgets: any[] = []
-    let from = 0
-    let to = state.doc.length-1 // TODO: could optimize this to just be lines within view
-    //let t = state.doc.toString()
-    //console.log(t);
-    let setpix = false;
-    let image = false;
+    // Creates a widget which accepts arguments of type T
+    function createWidget<T>(comp: React.ComponentType<any>, from: number, to: number, args: T[]) {      
+      args.forEach(function(value) { console.log(value); })
+      
+      let props = {
+        from: from,
+        to: to,
+        arguments: args
+      }
+      let deco = Decoration.widget({
+        widget: new Widget(comp, props, createPortal),
+        side: 1,
+      });
+    
+      widgets.push(deco.range(to));
+    }
+
     syntaxTree(state).iterate({
-      from, to,
       enter: (ref) => {
-        //console.log(ref.name);
-
-        // Found ArgList, will begin to parse nodes 
-        if(setpix && ref.name === "ArgList") {
-          let cs = ref.node.getChildren("Number");
-          if(cs.length === 3) {
-            widgets.push(createWidget<number>(
-              MicrobitSinglePixelComponent, 
-              ref.from, ref.to, 
-              numberArgs(state, cs),
-              createPortal).range(ref.to));
-            }
-          }
-        if(image && ref.name === "ArgList"){
-          let s = ref.node.getChild("ContinuedString");
+        // Found an ArgList, parent will be a CallExpression
+        if(ref.name === "ArgList" && ref.node.parent){
+          //console.log(state.doc.sliceString(ref.node.parent.from, ref.from));
           
-        }
+          // Match CallExpression name to our widgets
+          switch(state.doc.sliceString(ref.node.parent.from, ref.from)){
+            case "display.set_pixel":
+              // TODO: assuming all literals for now, will probably want a way to detect other types of arguments
+              let args: number[] = [];
+              ref.node.getChildren("Number").forEach( function(child) { args.push(+state.doc.sliceString(child.from, child.to)) }); 
 
-        // detected set_pixel, if next expression is an ArgList, show UI
-        setpix = ref.name === "PropertyName" && state.doc.sliceString(ref.from, ref.to) === "set_pixel"
-        if(setpix){
-          console.log(ref.node.nextSibling);
+              createWidget<number>(MicrobitSinglePixelComponent, ref.from, ref.to, args);
+              break;
+            case "Image":
+              // TODO: does not handle comments properly
+              let imArg: string[] = []
+              let arg = ref.node.getChild("ContinuedString");
+              if(arg) imArg.push(state.doc.sliceString(arg.from, arg.to).replaceAll(/[' \n]/g, ""));
+              else{
+                arg = ref.node.getChild("String");
+                if(arg) imArg.push()
+              } 
+              
+              createWidget<string>(MicrobitMultiplePixelComponent, ref.from, ref.to, imArg);
+              break;
+            default:
+              // No widget implemented for this function
+              break;
+          }
         }
-        image = ref.name === "VariableName" && state.doc.sliceString(ref.from, ref.to) === "Image"
       }
     })
 

From f1d3522f099999fc4c5e08c6bd97c83736cd34de Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sat, 23 Mar 2024 12:36:16 -0400
Subject: [PATCH 039/106] moved view into props

---
 .../helper-widgets/reactWidgetExtension.tsx   | 34 +++++++++++--------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index e50928a3c..4e643dd10 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -12,11 +12,13 @@ import {MicrobitMultiplePixelComponent, MicrobitSinglePixelComponent} from "./mi
 import { numberArgs } from "./argumentParser";
 
 interface WidgetProps<T>{
+  // Note: always an array, can be singleton
+  arguments : T[] 
   // Where to insert the changed values
   from : number,
   to : number,
-  // Note: always an array, can be singleton
-  arguments : T[] 
+  // Widget will change textfile
+  view: EditorView
 }
 
 /**
@@ -26,13 +28,22 @@ interface WidgetProps<T>{
 class Widget<T> extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private component : React.ComponentType<any>, private props: WidgetProps<T>, private createPortal: PortalFactory, ) {
+  constructor(private component : React.ComponentType<any>, 
+              private args: T[], private from: number, private to: number,
+              private createPortal: PortalFactory, ) {
     super();
   }
 
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
-    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { props: this.props, view: view }));
+    let props = {
+      arguments: this.args,
+      from: this.from,
+      to: this.to,
+      view: view
+    }
+
+    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { props: props }));
     return dom;
   }
 
@@ -54,16 +65,11 @@ export const reactWidgetExtension = (
   const decorate = (state: EditorState) => {
     let widgets: any[] = []
     // Creates a widget which accepts arguments of type T
-    function createWidget<T>(comp: React.ComponentType<any>, from: number, to: number, args: T[]) {      
+    function createWidget<T>(comp: React.ComponentType<any>, args: T[], from: number, to: number) {      
       args.forEach(function(value) { console.log(value); })
-      
-      let props = {
-        from: from,
-        to: to,
-        arguments: args
-      }
+
       let deco = Decoration.widget({
-        widget: new Widget(comp, props, createPortal),
+        widget: new Widget(comp, args, to, from, createPortal),
         side: 1,
       });
     
@@ -83,7 +89,7 @@ export const reactWidgetExtension = (
               let args: number[] = [];
               ref.node.getChildren("Number").forEach( function(child) { args.push(+state.doc.sliceString(child.from, child.to)) }); 
 
-              createWidget<number>(MicrobitSinglePixelComponent, ref.from, ref.to, args);
+              createWidget<number>(MicrobitSinglePixelComponent, args, ref.from, ref.to);
               break;
             case "Image":
               // TODO: does not handle comments properly
@@ -95,7 +101,7 @@ export const reactWidgetExtension = (
                 if(arg) imArg.push()
               } 
               
-              createWidget<string>(MicrobitMultiplePixelComponent, ref.from, ref.to, imArg);
+              createWidget<string>(MicrobitMultiplePixelComponent, imArg, ref.from, ref.to);
               break;
             default:
               // No widget implemented for this function

From ff395ddfb59fe18da764bc80f56a3977789a145c Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sat, 23 Mar 2024 12:37:40 -0400
Subject: [PATCH 040/106] fixed generic createWidget

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 4e643dd10..8b5ad32e2 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -69,7 +69,7 @@ export const reactWidgetExtension = (
       args.forEach(function(value) { console.log(value); })
 
       let deco = Decoration.widget({
-        widget: new Widget(comp, args, to, from, createPortal),
+        widget: new Widget<T>(comp, args, to, from, createPortal),
         side: 1,
       });
     

From a7f544d6abaf330cc7aad547d411d01d634784a7 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sat, 23 Mar 2024 21:07:39 +0100
Subject: [PATCH 041/106] changed look of pixels and name in interace, need to
 pass in view from reactwidgetextension

---
 .../helper-widgets/microbitWidget.tsx         | 169 ++++++------------
 .../helper-widgets/reactWidgetExtension.tsx   |   4 +-
 2 files changed, 58 insertions(+), 115 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/microbitWidget.tsx b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
index 920186497..de2ca352f 100644
--- a/src/editor/codemirror/helper-widgets/microbitWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
@@ -1,8 +1,6 @@
 import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
 import { useState } from "react";
-import {
-    EditorView,
-  } from "@codemirror/view";
+import {WidgetProps} from "./reactWidgetExtension";
 
 interface Pixel {
   x: number;
@@ -12,11 +10,9 @@ interface Pixel {
 
 interface MicrobitSinglePixelGridProps {
   onClickPixel: (pixel: Pixel) => void;
-  onSubmit: () => void;
-  isVisible: boolean;
 }
 
-const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onClickPixel, onSubmit, isVisible }) => {
+const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onClickPixel }) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
   const [brightness, setBrightness] = useState<number>(5);
 
@@ -33,97 +29,62 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
       onClickPixel(updatedPixel);
     }
   };
-  
+
   return (
-    <>
-      <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
-        <Box>
-          <Box bg="black" p="10px" borderRadius="5px">
-            {[...Array(5)].map((_, y) => (
-              <Box key={y} display="flex">
-                {[...Array(5)].map((_, x) => (
-                  <Box key={x} display="flex" mr="2px">
-                    <Button
-                      size="xs"
-                      h="15px"
-                      w="15px"
-                      p={0}
-                      bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
-                      _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
-                      onClick={() => handleClickPixel(x, y)}
-                    />
-                  </Box>
-                ))}
-              </Box>
-            ))}
-          </Box>
-          {selectedPixel && (
-            <Box display="flex" flexDirection="column" alignItems="center" mt="10px">
-              <Box bg="white" borderRadius="5px" p="5px" textAlign="center">
-                <Button onClick={() => onSubmit()} colorScheme="blue" size="sm">
-                  Looks Good
-                </Button>
-              </Box>
-            </Box>
-          )}
-          {!selectedPixel && (
-            <Box display="flex" flexDirection="column" alignItems="center" mt="10px">
-              <Box bg="white" borderRadius="5px" p="5px" textAlign="center">
-                <Button disabled colorScheme="blue" size="sm">
-                  Looks Good
-                </Button>
-              </Box>
+    <Box display="flex" flexDirection="row" justifyContent="flex-start">
+      <Box>
+        <Box bg="black" p="10px" borderRadius="5px">
+          {[...Array(5)].map((_, y) => (
+            <Box key={y} display="flex">
+              {[...Array(5)].map((_, x) => (
+                <Box key={x} display="flex" mr="2px">
+                  <Button
+                    size="xs"
+                    h="15px"
+                    w="15px"
+                    p={0}
+                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
+                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                    onClick={() => handleClickPixel(x, y)}
+                  />
+                </Box>
+              ))}
             </Box>
-          )}
+          ))}
         </Box>
-          <Box ml="10px">
-            <Slider
-              aria-label="brightness"
-              defaultValue={brightness}
-              min={0}
-              max={9}
-              step={1}
-              orientation="vertical"
-              _focus={{ boxShadow: "none" }}
-              _active={{ bgColor: "transparent" }}
-              onChange={handleSliderChange}>
-              <SliderTrack>
-                <SliderFilledTrack />
-              </SliderTrack>
-              <SliderThumb />
-            </Slider>
-          </Box>
       </Box>
-      {selectedPixel && (
-        <Box mt="4px">
-          <span style={{ fontSize: "small" }}>
-            Selected pixel: ({selectedPixel.x}, {selectedPixel.y}) | Brightness: {selectedPixel.brightness}
-          </span>
-        </Box>
-      )}
-      {!selectedPixel && (
-        <Box mt="4px">
-          <span style={{ fontSize: "small" }}>
-          Select a pixel
-          </span>
-        </Box>
-      )}
-    </>
+      <Box ml="10px">
+        <Slider
+          aria-label="brightness"
+          defaultValue={brightness}
+          min={0}
+          max={9}
+          step={1}
+          orientation="vertical"
+          _focus={{ boxShadow: "none" }}
+          _active={{ bgColor: "transparent" }}
+          onChange={handleSliderChange}>
+          <SliderTrack>
+            <SliderFilledTrack />
+          </SliderTrack>
+          <SliderThumb />
+        </Slider>
+      </Box>
+    </Box>
   );
 };
 
-export const MicrobitSinglePixelComponent = ({ from, to, view }: { from: number, to: number, view: EditorView }) => {
-  const [isVisible, setIsVisible] = useState(true);
+export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+  if (Array.isArray(args) && args.length === 3) {
+    const [x, y, brightness] = args;
+    setSelectedPixel({ x, y, brightness });
+  }
 
   const handleSelectPixel = (pixel: Pixel) => {
     setSelectedPixel(pixel);
-  };
-
-  const handleSubmit = () => {
     if (selectedPixel !== null) {
       const { x, y, brightness } = selectedPixel;
-      setIsVisible(false);      
       view.dispatch({
         changes: {
           from: from,
@@ -134,28 +95,26 @@ export const MicrobitSinglePixelComponent = ({ from, to, view }: { from: number,
     }
   };
 
-  return (<MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} onSubmit={handleSubmit} isVisible={isVisible} />);
+  return (<MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} />);
 };
 
+
 interface MultiMicrobitGridProps {
   selectedPixels: Pixel[];
   onPixelClick: (x: number, y: number) => void;
   onBrightnessChange: (x: number, y: number, brightness: number) => void;
   onSubmit: () => void;
-  isVisible: boolean;
-  currentBrightness : number;
+  currentBrightness: number;
 }
 
 const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   selectedPixels,
   onPixelClick,
   onBrightnessChange,
-  onSubmit,
-  isVisible,
   currentBrightness
 }) => {
   return (
-    <Box display={isVisible ? "flex" : "none"} flexDirection="row" justifyContent="flex-start">
+    <Box display="flex" flexDirection="row" justifyContent="flex-start">
       <Box>
         <Box bg="black" p="10px" borderRadius="5px">
           {[...Array(5)].map((_, y) => (
@@ -176,13 +135,6 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
             </Box>
           ))}
         </Box>
-        <Box display="flex" justifyContent="center" mt="10px">
-          <Box bg="white" borderRadius="5px" p="5px">
-            <Button onClick={onSubmit} colorScheme="blue" size="sm">
-              Looks Good
-            </Button>
-          </Box>
-        </Box>
       </Box>
       <Box ml="10px">
         <Slider
@@ -208,19 +160,10 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent = ({ from, to, view }: { from: number; to: number; view: EditorView; }) => {
+export const MicrobitMultiplePixelComponent = ({args, from, to, view }: WidgetProps<number>) => {
   const initialSelectedPixels: Pixel[] = [];
-  /*
-  //Probably unnecessary to intialize the state, we can set it to 0 in the arguments by default anyway and it messes up some other logic
-  for (let x = 0; x <= 4; x++) {
-    for (let y = 0; y <= 4; y++) {
-      initialSelectedPixels.push({ x, y, brightness: 0 });
-    }
-  }
-  */
 
   const [selectedPixels, setSelectedPixels] = useState<Pixel[]>(initialSelectedPixels);
-  const [isVisible, setIsVisible] = useState(true);
   const [currentBrightness, setCurrentBrightness] = useState(5);
 
   const handlePixelClick = (x: number, y: number) => {
@@ -233,11 +176,7 @@ export const MicrobitMultiplePixelComponent = ({ from, to, view }: { from: numbe
       const newPixel: Pixel = { x, y, brightness: currentBrightness };
       setSelectedPixels([...selectedPixels, newPixel]);
     }
-  };
-
-  const handleSubmit = () => {
-    //add the logic to change the arguments to the function 
-    setIsVisible(false);
+    handleSubmit();
   };
 
   const handleBrightnessChange = (x: number, y: number, brightness: number) => {
@@ -250,6 +189,11 @@ export const MicrobitMultiplePixelComponent = ({ from, to, view }: { from: numbe
       }
       return updatedPixels;
     });
+    handleSubmit();
+  };
+
+  const handleSubmit = () => {
+    console.log("Submitting...");
   };
 
   return (
@@ -258,7 +202,6 @@ export const MicrobitMultiplePixelComponent = ({ from, to, view }: { from: numbe
       onPixelClick={handlePixelClick}
       onBrightnessChange={handleBrightnessChange}
       onSubmit={handleSubmit}
-      isVisible={isVisible}
       currentBrightness={currentBrightness}
     />
   );
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 8b5ad32e2..9c34a5137 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -11,9 +11,9 @@ import React from "react";
 import {MicrobitMultiplePixelComponent, MicrobitSinglePixelComponent} from "./microbitWidget";
 import { numberArgs } from "./argumentParser";
 
-interface WidgetProps<T>{
+export interface WidgetProps<T>{
   // Note: always an array, can be singleton
-  arguments : T[] 
+  args : T[] 
   // Where to insert the changed values
   from : number,
   to : number,

From c35a9a8cfdd3fbc1a582992ae2c6af91c01261fc Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sat, 23 Mar 2024 23:19:58 -0400
Subject: [PATCH 042/106] renaming

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 9c34a5137..efd042804 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -37,13 +37,13 @@ class Widget<T> extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
     let props = {
-      arguments: this.args,
+      args: this.args,
       from: this.from,
       to: this.to,
       view: view
     }
 
-    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { props: props }));
+    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { props }));
     return dom;
   }
 

From 62a147fe5acb8d7c3484f7aea1dcc7925fc00765 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sat, 23 Mar 2024 23:37:10 -0400
Subject: [PATCH 043/106] fixed props.view

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index efd042804..f846bcea1 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -43,7 +43,7 @@ class Widget<T> extends WidgetType {
       view: view
     }
 
-    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, { props }));
+    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, props));
     return dom;
   }
 

From c0b378d5b51199241297170205dfb4dfd3c931c9 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 24 Mar 2024 09:39:28 +0100
Subject: [PATCH 044/106] should work, gets invalid isnertion range or
 something idk

---
 .../helper-widgets/microbitWidget.tsx         | 29 ++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/microbitWidget.tsx b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
index de2ca352f..e1846c946 100644
--- a/src/editor/codemirror/helper-widgets/microbitWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
@@ -85,6 +85,7 @@ export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetPro
     setSelectedPixel(pixel);
     if (selectedPixel !== null) {
       const { x, y, brightness } = selectedPixel;
+      console.log(`(${x}, ${y}, ${brightness}) `);
       view.dispatch({
         changes: {
           from: from,
@@ -160,6 +161,25 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
+
+function pixelsToString(pixels: Pixel[]): string {
+  let outputString = '';
+  for (let y = 0; y < 5; y++) {
+      for (let x = 0; x < 5; x++) {
+          const pixel = pixels.find(p => p.x === x && p.y === y);
+          if (pixel) {
+              outputString += pixel.brightness.toString();
+          } else {
+              outputString += '0';
+          }
+      }
+      outputString += ':';
+  }
+  outputString = outputString.slice(0, -1);
+
+  return outputString;
+}
+
 export const MicrobitMultiplePixelComponent = ({args, from, to, view }: WidgetProps<number>) => {
   const initialSelectedPixels: Pixel[] = [];
 
@@ -193,7 +213,14 @@ export const MicrobitMultiplePixelComponent = ({args, from, to, view }: WidgetPr
   };
 
   const handleSubmit = () => {
-    console.log("Submitting...");
+    let insertion = pixelsToString(selectedPixels);
+    console.log(insertion)
+    view.dispatch({
+      changes: {
+        from: from,
+        to: to,
+        insert: insertion}
+      });
   };
 
   return (

From 3199b945f684155833c94cf6486c8669665492ce Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 24 Mar 2024 11:53:07 +0100
Subject: [PATCH 045/106] okay im done with the frontend, just need to add
 correct initialization and fix the insertions

---
 .../helper-widgets/microbitWidget.tsx         | 125 ++++++++----------
 1 file changed, 53 insertions(+), 72 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/microbitWidget.tsx b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
index e1846c946..cb0659131 100644
--- a/src/editor/codemirror/helper-widgets/microbitWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/microbitWidget.tsx
@@ -85,14 +85,15 @@ export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetPro
     setSelectedPixel(pixel);
     if (selectedPixel !== null) {
       const { x, y, brightness } = selectedPixel;
-      console.log(`(${x}, ${y}, ${brightness}) `);
-      view.dispatch({
+      console.log(`(${x}, ${y}, ${brightness}) `)
+      /*view.dispatch({
         changes: {
           from: from,
           to: to,
           insert: `(${x}, ${y}, ${brightness}) `,
         }
       });
+      */
     }
   };
 
@@ -101,35 +102,45 @@ export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetPro
 
 
 interface MultiMicrobitGridProps {
-  selectedPixels: Pixel[];
-  onPixelClick: (x: number, y: number) => void;
-  onBrightnessChange: (x: number, y: number, brightness: number) => void;
-  onSubmit: () => void;
-  currentBrightness: number;
+  selectedPixels: number[][];
+  onPixelChange: (x: number, y: number, brightness: number) => void;
 }
 
 const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   selectedPixels,
-  onPixelClick,
-  onBrightnessChange,
-  currentBrightness
+  onPixelChange,
 }) => {
+  const [currentBrightness, setCurrentBrightness] = useState<number>(5);
+  const [selectedPixel, setSelectedPixel] = useState<{ x: number; y: number } | null>(null);
+
+  const handlePixelClick = (x: number, y: number) => {
+    setSelectedPixel({ x, y });
+    onPixelChange(x, y, currentBrightness);
+  };
+
+  const handleBrightnessChange = (brightness: number) => {
+    setCurrentBrightness(brightness);
+    if (selectedPixel) {
+      onPixelChange(selectedPixel.x, selectedPixel.y, brightness);
+    }
+  };
+
   return (
     <Box display="flex" flexDirection="row" justifyContent="flex-start">
       <Box>
         <Box bg="black" p="10px" borderRadius="5px">
-          {[...Array(5)].map((_, y) => (
+          {selectedPixels.map((row, y) => (
             <Box key={y} display="flex">
-              {[...Array(5)].map((_, x) => (
+              {row.map((brightness, x) => (
                 <Box key={x} display="flex" mr="2px">
                   <Button
                     size="xs"
                     h="15px"
                     w="15px"
                     p={0}
-                    bgColor={selectedPixels.some(p => p.x === x && p.y === y) ? `rgba(255, 0, 0, ${(selectedPixels.find(p => p.x === x && p.y === y)!.brightness) / 9})` : "rgba(255, 255, 255, 0)"}
-                    _hover={{ bgColor: selectedPixels.some(p => p.x === x && p.y === y) ? `rgba(255, 0, 0, ${(selectedPixels.find(p => p.x === x && p.y === y)!.brightness) / 9})` : "rgba(255, 255, 255, 0.5)" }}
-                    onClick={() => onPixelClick(x, y)}
+                    bgColor={`rgba(255, 0, 0, ${brightness / 9})`}
+                    _hover={{ bgColor: brightness > 0 ? `rgba(255, 0, 0, ${brightness / 9} + 0.1)` : "rgba(255, 255, 255, 0.5)" }}
+                    onClick={() => handlePixelClick(x, y)}
                   />
                 </Box>
               ))}
@@ -147,10 +158,8 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
           orientation="vertical"
           _focus={{ boxShadow: "none" }}
           _active={{ bgColor: "transparent" }}
-          onChange={(value) => {
-            const lastPixel = selectedPixels.length > 0 ? selectedPixels[selectedPixels.length - 1] : { x: -1, y: -1 };
-            onBrightnessChange(lastPixel.x, lastPixel.y, value);
-          }}        >
+          onChange={(value) => handleBrightnessChange(value)}
+        >
           <SliderTrack>
             <SliderFilledTrack />
           </SliderTrack>
@@ -162,74 +171,46 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
 };
 
 
-function pixelsToString(pixels: Pixel[]): string {
-  let outputString = '';
-  for (let y = 0; y < 5; y++) {
-      for (let x = 0; x < 5; x++) {
-          const pixel = pixels.find(p => p.x === x && p.y === y);
-          if (pixel) {
-              outputString += pixel.brightness.toString();
-          } else {
-              outputString += '0';
-          }
-      }
-      outputString += ':';
-  }
-  outputString = outputString.slice(0, -1);
-
-  return outputString;
-}
+export const MicrobitMultiplePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
+  const initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
 
-export const MicrobitMultiplePixelComponent = ({args, from, to, view }: WidgetProps<number>) => {
-  const initialSelectedPixels: Pixel[] = [];
+  const [selectedPixels, setSelectedPixels] = useState<number[][]>(initialSelectedPixels);
 
-  const [selectedPixels, setSelectedPixels] = useState<Pixel[]>(initialSelectedPixels);
-  const [currentBrightness, setCurrentBrightness] = useState(5);
-
-  const handlePixelClick = (x: number, y: number) => {
-    const existingIndex = selectedPixels.findIndex(pixel => pixel.x === x && pixel.y === y);
-    if (existingIndex !== -1) {
-      const updatedPixels = [...selectedPixels];
-      updatedPixels[existingIndex].brightness = currentBrightness;
-      setSelectedPixels(updatedPixels);
-    } else {
-      const newPixel: Pixel = { x, y, brightness: currentBrightness };
-      setSelectedPixels([...selectedPixels, newPixel]);
-    }
-    handleSubmit();
-  };
-
-  const handleBrightnessChange = (x: number, y: number, brightness: number) => {
-    setCurrentBrightness(brightness);
-    setSelectedPixels(prevPixels => {
-      const updatedPixels = [...prevPixels];
-      const pixelIndex = updatedPixels.findIndex(pixel => pixel.x === x && pixel.y === y);
-      if (pixelIndex !== -1) {
-        updatedPixels[pixelIndex].brightness = brightness;
-      }
-      return updatedPixels;
-    });
+  const handlePixelChange = (x: number, y: number, brightness: number) => {
+    const updatedPixels = [...selectedPixels];
+    updatedPixels[y][x] = brightness;
+    setSelectedPixels(updatedPixels);
     handleSubmit();
   };
 
   const handleSubmit = () => {
     let insertion = pixelsToString(selectedPixels);
-    console.log(insertion)
-    view.dispatch({
+    console.log(insertion);
+    /*view.dispatch({
       changes: {
         from: from,
         to: to,
         insert: insertion}
       });
-  };
+    */
+  }
 
   return (
     <MicrobitMultiplePixelsGrid
       selectedPixels={selectedPixels}
-      onPixelClick={handlePixelClick}
-      onBrightnessChange={handleBrightnessChange}
-      onSubmit={handleSubmit}
-      currentBrightness={currentBrightness}
+      onPixelChange={handlePixelChange}
     />
   );
-};
\ No newline at end of file
+};
+
+function pixelsToString(pixels: number[][]): string {
+  let outputString = '';
+  for (let y = 0; y < 5; y++) {
+    for (let x = 0; x < 5; x++) {
+      outputString += pixels[y][x].toString();
+    }
+    outputString += ':';
+  }
+  outputString = outputString.slice(0, -1);
+  return outputString;
+}
\ No newline at end of file

From 6a11655a5a3da7fbc8517fb4d550a3abb6997597 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 24 Mar 2024 12:10:20 +0100
Subject: [PATCH 046/106] reorganized

---
 .../helper-widgets/argumentParser.tsx         |   4 +-
 .../helper-widgets/reactWidgetExtension.tsx   |  43 ++++----
 .../helper-widgets/setPixelWidget.tsx         | 101 +++++++++++++++++
 ...microbitWidget.tsx => showImageWidget.tsx} | 103 +-----------------
 4 files changed, 127 insertions(+), 124 deletions(-)
 create mode 100644 src/editor/codemirror/helper-widgets/setPixelWidget.tsx
 rename src/editor/codemirror/helper-widgets/{microbitWidget.tsx => showImageWidget.tsx} (52%)

diff --git a/src/editor/codemirror/helper-widgets/argumentParser.tsx b/src/editor/codemirror/helper-widgets/argumentParser.tsx
index fa5bf1e42..e5d88be25 100644
--- a/src/editor/codemirror/helper-widgets/argumentParser.tsx
+++ b/src/editor/codemirror/helper-widgets/argumentParser.tsx
@@ -7,7 +7,7 @@ import { EditorState } from "@codemirror/state";
 export function numberArgs(state: EditorState, args: any[]): number[] {
     let nums = []
     args.forEach(function (value) {
-        
-    }); 
+
+    });
     return []
 }
\ No newline at end of file
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index f846bcea1..036013643 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -8,15 +8,16 @@ import {
 import { syntaxTree } from "@codemirror/language"
 import { PortalFactory } from "../CodeMirror";
 import React from "react";
-import {MicrobitMultiplePixelComponent, MicrobitSinglePixelComponent} from "./microbitWidget";
-import { numberArgs } from "./argumentParser";
+import { MicrobitSinglePixelComponent } from "./setPixelWidget";
+import { MicrobitMultiplePixelComponent } from "./showImageWidget"
+//import { numberArgs } from "./argumentParser";
 
-export interface WidgetProps<T>{
+export interface WidgetProps<T> {
   // Note: always an array, can be singleton
-  args : T[] 
+  args: T[]
   // Where to insert the changed values
-  from : number,
-  to : number,
+  from: number,
+  to: number,
   // Widget will change textfile
   view: EditorView
 }
@@ -28,9 +29,9 @@ export interface WidgetProps<T>{
 class Widget<T> extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private component : React.ComponentType<any>, 
-              private args: T[], private from: number, private to: number,
-              private createPortal: PortalFactory, ) {
+  constructor(private component: React.ComponentType<any>,
+    private args: T[], private from: number, private to: number,
+    private createPortal: PortalFactory,) {
     super();
   }
 
@@ -65,29 +66,29 @@ export const reactWidgetExtension = (
   const decorate = (state: EditorState) => {
     let widgets: any[] = []
     // Creates a widget which accepts arguments of type T
-    function createWidget<T>(comp: React.ComponentType<any>, args: T[], from: number, to: number) {      
-      args.forEach(function(value) { console.log(value); })
+    function createWidget<T>(comp: React.ComponentType<any>, args: T[], from: number, to: number) {
+      args.forEach(function (value) { console.log(value); })
 
       let deco = Decoration.widget({
         widget: new Widget<T>(comp, args, to, from, createPortal),
         side: 1,
       });
-    
+
       widgets.push(deco.range(to));
     }
 
     syntaxTree(state).iterate({
       enter: (ref) => {
         // Found an ArgList, parent will be a CallExpression
-        if(ref.name === "ArgList" && ref.node.parent){
+        if (ref.name === "ArgList" && ref.node.parent) {
           //console.log(state.doc.sliceString(ref.node.parent.from, ref.from));
-          
+
           // Match CallExpression name to our widgets
-          switch(state.doc.sliceString(ref.node.parent.from, ref.from)){
+          switch (state.doc.sliceString(ref.node.parent.from, ref.from)) {
             case "display.set_pixel":
               // TODO: assuming all literals for now, will probably want a way to detect other types of arguments
               let args: number[] = [];
-              ref.node.getChildren("Number").forEach( function(child) { args.push(+state.doc.sliceString(child.from, child.to)) }); 
+              ref.node.getChildren("Number").forEach(function (child) { args.push(+state.doc.sliceString(child.from, child.to)) });
 
               createWidget<number>(MicrobitSinglePixelComponent, args, ref.from, ref.to);
               break;
@@ -95,12 +96,12 @@ export const reactWidgetExtension = (
               // TODO: does not handle comments properly
               let imArg: string[] = []
               let arg = ref.node.getChild("ContinuedString");
-              if(arg) imArg.push(state.doc.sliceString(arg.from, arg.to).replaceAll(/[' \n]/g, ""));
-              else{
+              if (arg) imArg.push(state.doc.sliceString(arg.from, arg.to).replaceAll(/[' \n]/g, ""));
+              else {
                 arg = ref.node.getChild("String");
-                if(arg) imArg.push()
-              } 
-              
+                if (arg) imArg.push()
+              }
+
               createWidget<string>(MicrobitMultiplePixelComponent, imArg, ref.from, ref.to);
               break;
             default:
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
new file mode 100644
index 000000000..096df32bd
--- /dev/null
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -0,0 +1,101 @@
+import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
+import { useState } from "react";
+import { WidgetProps } from "./reactWidgetExtension";
+
+interface Pixel {
+  x: number;
+  y: number;
+  brightness: number;
+}
+
+interface MicrobitSinglePixelGridProps {
+  onClickPixel: (pixel: Pixel) => void;
+}
+
+const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onClickPixel }) => {
+  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+  const [brightness, setBrightness] = useState<number>(5);
+
+  const handleClickPixel = (x: number, y: number) => {
+    const newPixel: Pixel = { x, y, brightness };
+    setSelectedPixel(newPixel);
+    onClickPixel(newPixel);
+  };
+
+  const handleSliderChange = (value: number) => {
+    setBrightness(value);
+    if (selectedPixel) {
+      const updatedPixel: Pixel = { ...selectedPixel, brightness: value };
+      onClickPixel(updatedPixel);
+    }
+  };
+
+  return (
+    <Box display="flex" flexDirection="row" justifyContent="flex-start">
+      <Box>
+        <Box bg="black" p="10px" borderRadius="5px">
+          {[...Array(5)].map((_, y) => (
+            <Box key={y} display="flex">
+              {[...Array(5)].map((_, x) => (
+                <Box key={x} display="flex" mr="2px">
+                  <Button
+                    size="xs"
+                    h="15px"
+                    w="15px"
+                    p={0}
+                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
+                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                    onClick={() => handleClickPixel(x, y)}
+                  />
+                </Box>
+              ))}
+            </Box>
+          ))}
+        </Box>
+      </Box>
+      <Box ml="10px">
+        <Slider
+          aria-label="brightness"
+          defaultValue={brightness}
+          min={0}
+          max={9}
+          step={1}
+          orientation="vertical"
+          _focus={{ boxShadow: "none" }}
+          _active={{ bgColor: "transparent" }}
+          onChange={handleSliderChange}>
+          <SliderTrack>
+            <SliderFilledTrack />
+          </SliderTrack>
+          <SliderThumb />
+        </Slider>
+      </Box>
+    </Box>
+  );
+};
+
+export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
+  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+  if (Array.isArray(args) && args.length === 3) {
+    const [x, y, brightness] = args;
+    setSelectedPixel({ x, y, brightness });
+  }
+
+  const handleSelectPixel = (pixel: Pixel) => {
+    setSelectedPixel(pixel);
+    if (selectedPixel !== null) {
+      const { x, y, brightness } = selectedPixel;
+      console.log(`(${x}, ${y}, ${brightness}) `)
+      /*view.dispatch({
+        changes: {
+          from: from,
+          to: to,
+          insert: `(${x}, ${y}, ${brightness}) `,
+        }
+      });
+      */
+    }
+  };
+
+  return (<MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} />);
+};
diff --git a/src/editor/codemirror/helper-widgets/microbitWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
similarity index 52%
rename from src/editor/codemirror/helper-widgets/microbitWidget.tsx
rename to src/editor/codemirror/helper-widgets/showImageWidget.tsx
index cb0659131..67039b38e 100644
--- a/src/editor/codemirror/helper-widgets/microbitWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -1,105 +1,6 @@
 import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
-import { useState } from "react";
-import {WidgetProps} from "./reactWidgetExtension";
-
-interface Pixel {
-  x: number;
-  y: number;
-  brightness: number;
-}
-
-interface MicrobitSinglePixelGridProps {
-  onClickPixel: (pixel: Pixel) => void;
-}
-
-const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onClickPixel }) => {
-  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-  const [brightness, setBrightness] = useState<number>(5);
-
-  const handleClickPixel = (x: number, y: number) => {
-    const newPixel: Pixel = { x, y, brightness };
-    setSelectedPixel(newPixel);
-    onClickPixel(newPixel);
-  };
-
-  const handleSliderChange = (value: number) => {
-    if (selectedPixel) {
-      setBrightness(value);
-      const updatedPixel: Pixel = { ...selectedPixel, brightness: value };
-      onClickPixel(updatedPixel);
-    }
-  };
-
-  return (
-    <Box display="flex" flexDirection="row" justifyContent="flex-start">
-      <Box>
-        <Box bg="black" p="10px" borderRadius="5px">
-          {[...Array(5)].map((_, y) => (
-            <Box key={y} display="flex">
-              {[...Array(5)].map((_, x) => (
-                <Box key={x} display="flex" mr="2px">
-                  <Button
-                    size="xs"
-                    h="15px"
-                    w="15px"
-                    p={0}
-                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
-                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
-                    onClick={() => handleClickPixel(x, y)}
-                  />
-                </Box>
-              ))}
-            </Box>
-          ))}
-        </Box>
-      </Box>
-      <Box ml="10px">
-        <Slider
-          aria-label="brightness"
-          defaultValue={brightness}
-          min={0}
-          max={9}
-          step={1}
-          orientation="vertical"
-          _focus={{ boxShadow: "none" }}
-          _active={{ bgColor: "transparent" }}
-          onChange={handleSliderChange}>
-          <SliderTrack>
-            <SliderFilledTrack />
-          </SliderTrack>
-          <SliderThumb />
-        </Slider>
-      </Box>
-    </Box>
-  );
-};
-
-export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
-  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-  if (Array.isArray(args) && args.length === 3) {
-    const [x, y, brightness] = args;
-    setSelectedPixel({ x, y, brightness });
-  }
-
-  const handleSelectPixel = (pixel: Pixel) => {
-    setSelectedPixel(pixel);
-    if (selectedPixel !== null) {
-      const { x, y, brightness } = selectedPixel;
-      console.log(`(${x}, ${y}, ${brightness}) `)
-      /*view.dispatch({
-        changes: {
-          from: from,
-          to: to,
-          insert: `(${x}, ${y}, ${brightness}) `,
-        }
-      });
-      */
-    }
-  };
-
-  return (<MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} />);
-};
-
+import React, { useState } from "react";
+import { WidgetProps } from "./reactWidgetExtension";
 
 interface MultiMicrobitGridProps {
   selectedPixels: number[][];

From 7fbe7cc211585019094f007455b0bd161e942b65 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 24 Mar 2024 14:04:47 +0100
Subject: [PATCH 047/106] ye

---
 .../helper-widgets/setPixelWidget.tsx         | 32 +++++++++++--------
 .../helper-widgets/showImageWidget.tsx        |  4 +--
 2 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 096df32bd..62ce88b94 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -9,24 +9,24 @@ interface Pixel {
 }
 
 interface MicrobitSinglePixelGridProps {
-  onClickPixel: (pixel: Pixel) => void;
+  onPixelClick: (pixel: Pixel) => void;
 }
 
-const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onClickPixel }) => {
+const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPixelClick }) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-  const [brightness, setBrightness] = useState<number>(5);
+  const [currentBrightness, setCurrentBrightness] = useState<number>(5);
 
-  const handleClickPixel = (x: number, y: number) => {
-    const newPixel: Pixel = { x, y, brightness };
+  const handlePixelClick = (x: number, y: number) => {
+    const newPixel: Pixel = { x: x, y: y, brightness: currentBrightness };
     setSelectedPixel(newPixel);
-    onClickPixel(newPixel);
+    onPixelClick(newPixel);
   };
 
   const handleSliderChange = (value: number) => {
-    setBrightness(value);
+    setCurrentBrightness(value);
     if (selectedPixel) {
       const updatedPixel: Pixel = { ...selectedPixel, brightness: value };
-      onClickPixel(updatedPixel);
+      onPixelClick(updatedPixel);
     }
   };
 
@@ -43,9 +43,9 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
                     h="15px"
                     w="15px"
                     p={0}
-                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0)"}
-                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${brightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
-                    onClick={() => handleClickPixel(x, y)}
+                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${currentBrightness / 9})` : "rgba(255, 255, 255, 0)"}
+                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${currentBrightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                    onClick={() => handlePixelClick(x, y)}
                   />
                 </Box>
               ))}
@@ -56,7 +56,7 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onCli
       <Box ml="10px">
         <Slider
           aria-label="brightness"
-          defaultValue={brightness}
+          defaultValue={currentBrightness}
           min={0}
           max={9}
           step={1}
@@ -83,9 +83,13 @@ export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetPro
 
   const handleSelectPixel = (pixel: Pixel) => {
     setSelectedPixel(pixel);
+    updateView();
+  };
+
+  const updateView = () => {
     if (selectedPixel !== null) {
       const { x, y, brightness } = selectedPixel;
-      console.log(`(${x}, ${y}, ${brightness}) `)
+      console.log(`(${x}, ${y}, ${brightness}) `);
       /*view.dispatch({
         changes: {
           from: from,
@@ -97,5 +101,5 @@ export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetPro
     }
   };
 
-  return (<MicrobitSinglePixelGrid onClickPixel={handleSelectPixel} />);
+  return (<MicrobitSinglePixelGrid onPixelClick={handleSelectPixel} />);
 };
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 67039b38e..6511a9160 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -81,10 +81,10 @@ export const MicrobitMultiplePixelComponent = ({ args, from, to, view }: WidgetP
     const updatedPixels = [...selectedPixels];
     updatedPixels[y][x] = brightness;
     setSelectedPixels(updatedPixels);
-    handleSubmit();
+    updateView();
   };
 
-  const handleSubmit = () => {
+  const updateView = () => {
     let insertion = pixelsToString(selectedPixels);
     console.log(insertion);
     /*view.dispatch({

From 00f833d93c3fe57e6b5b49f412fc6a885c04b6f8 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 24 Mar 2024 21:00:37 +0100
Subject: [PATCH 048/106] .

---
 .../helper-widgets/setPixelWidget.tsx         | 22 ++++++++++++++-----
 .../helper-widgets/showImageWidget.tsx        | 18 +++++++++++----
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 62ce88b94..76174186e 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -10,10 +10,11 @@ interface Pixel {
 
 interface MicrobitSinglePixelGridProps {
   onPixelClick: (pixel: Pixel) => void;
+  initialPixel : Pixel | null;
 }
 
-const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPixelClick }) => {
-  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
+const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPixelClick, initialPixel }) => {
+  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(initialPixel);
   const [currentBrightness, setCurrentBrightness] = useState<number>(5);
 
   const handlePixelClick = (x: number, y: number) => {
@@ -74,12 +75,21 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPix
   );
 };
 
+const parseArgs = (args : number[]) => {
+  return args
+};
+
+const validateArgs = (args : number[]) => {
+  return Array.isArray(args) && args.length === 3
+};
+
+
 export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-  if (Array.isArray(args) && args.length === 3) {
-    const [x, y, brightness] = args;
+  if (validateArgs(args)){
+    const [x, y, brightness] = parseArgs(args);
     setSelectedPixel({ x, y, brightness });
-  }
+  } 
 
   const handleSelectPixel = (pixel: Pixel) => {
     setSelectedPixel(pixel);
@@ -101,5 +111,5 @@ export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetPro
     }
   };
 
-  return (<MicrobitSinglePixelGrid onPixelClick={handleSelectPixel} />);
+  return (<MicrobitSinglePixelGrid onPixelClick={handleSelectPixel} initialPixel={selectedPixel} />);
 };
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 6511a9160..156851560 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -40,7 +40,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
                     w="15px"
                     p={0}
                     bgColor={`rgba(255, 0, 0, ${brightness / 9})`}
-                    _hover={{ bgColor: brightness > 0 ? `rgba(255, 0, 0, ${brightness / 9} + 0.1)` : "rgba(255, 255, 255, 0.5)" }}
+                    _hover={{ bgColor: brightness > 0 ? `rgba(255, 100, 100, ${selectedPixels[y][x] / 9})` : "rgba(255, 255, 255, 0.5)" }}
                     onClick={() => handlePixelClick(x, y)}
                   />
                 </Box>
@@ -71,10 +71,11 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-
 export const MicrobitMultiplePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
-  const initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
-
+  let initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
+  if (validateArgs(args)) {
+    initialSelectedPixels = parseArgs(args)
+  }
   const [selectedPixels, setSelectedPixels] = useState<number[][]>(initialSelectedPixels);
 
   const handlePixelChange = (x: number, y: number, brightness: number) => {
@@ -104,6 +105,15 @@ export const MicrobitMultiplePixelComponent = ({ args, from, to, view }: WidgetP
   );
 };
 
+const parseArgs = (args: number[]) => {
+  return []
+};
+
+const validateArgs = (args: number[]) => {
+  return false
+};
+
+
 function pixelsToString(pixels: number[][]): string {
   let outputString = '';
   for (let y = 0; y < 5; y++) {

From 7be22fffb212fccffcdd4dc6646ed1b49a707d44 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 26 Mar 2024 09:43:02 -0400
Subject: [PATCH 049/106] syntaxNode argument

---
 src/editor/codemirror/helper-widgets/argumentParser.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/argumentParser.tsx b/src/editor/codemirror/helper-widgets/argumentParser.tsx
index e5d88be25..7e4b79f9f 100644
--- a/src/editor/codemirror/helper-widgets/argumentParser.tsx
+++ b/src/editor/codemirror/helper-widgets/argumentParser.tsx
@@ -1,10 +1,10 @@
 import { EditorState } from "@codemirror/state";
-
+import { SyntaxNode } from "@lezer/common";
 // TODO: might move parsing to here once arguments are no longer literals
 // see index.d.ts
 
 // Pre: args are all of type number
-export function numberArgs(state: EditorState, args: any[]): number[] {
+export function numberArgs(state: EditorState, args: any[], node:SyntaxNode): number[] {
     let nums = []
     args.forEach(function (value) {
 

From dd120844dab1ddbb7fcf7ea24206ecf460b287a9 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 26 Mar 2024 10:18:10 -0400
Subject: [PATCH 050/106] from to

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 036013643..20177d1bb 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -70,7 +70,7 @@ export const reactWidgetExtension = (
       args.forEach(function (value) { console.log(value); })
 
       let deco = Decoration.widget({
-        widget: new Widget<T>(comp, args, to, from, createPortal),
+        widget: new Widget<T>(comp, args, from, to, createPortal),
         side: 1,
       });
 

From 902bb1c13420d117cdbf2c499839d34ef387ebaf Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 26 Mar 2024 10:33:00 -0400
Subject: [PATCH 051/106] updates to props

removed view from props,
added ranges to props
---
 .../helper-widgets/reactWidgetExtension.tsx   | 26 +++++++++----------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 20177d1bb..7b673cbc8 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -15,11 +15,11 @@ import { MicrobitMultiplePixelComponent } from "./showImageWidget"
 export interface WidgetProps<T> {
   // Note: always an array, can be singleton
   args: T[]
+  // Ranges of where to insert each argument
+  ranges: {from:number, to:number} []
   // Where to insert the changed values
   from: number,
-  to: number,
-  // Widget will change textfile
-  view: EditorView
+  to: number
 }
 
 /**
@@ -30,21 +30,15 @@ class Widget<T> extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
   constructor(private component: React.ComponentType<any>,
-    private args: T[], private from: number, private to: number,
-    private createPortal: PortalFactory,) {
+    private props:WidgetProps<T>,
+    private createPortal: PortalFactory) {
     super();
   }
 
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
-    let props = {
-      args: this.args,
-      from: this.from,
-      to: this.to,
-      view: view
-    }
 
-    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, props));
+    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
     return dom;
   }
 
@@ -68,9 +62,15 @@ export const reactWidgetExtension = (
     // Creates a widget which accepts arguments of type T
     function createWidget<T>(comp: React.ComponentType<any>, args: T[], from: number, to: number) {
       args.forEach(function (value) { console.log(value); })
+      let props = {
+        args: args,
+        ranges: [],
+        from: from,
+        to: to,
+      }
 
       let deco = Decoration.widget({
-        widget: new Widget<T>(comp, args, from, to, createPortal),
+        widget: new Widget<T>(comp, props, createPortal),
         side: 1,
       });
 

From 6c240095e0ff4d13d1e824d95a599cef80339317 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sat, 30 Mar 2024 14:19:23 -0400
Subject: [PATCH 052/106] Better syntax tree iteration

handle parsing in a separate file, and allows for insertion by range
---
 .../helper-widgets/argumentParser.tsx         | 13 ----
 .../helper-widgets/reactWidgetExtension.tsx   | 64 +++++------------
 .../helper-widgets/widgetArgParser.tsx        | 72 +++++++++++++++++++
 3 files changed, 88 insertions(+), 61 deletions(-)
 delete mode 100644 src/editor/codemirror/helper-widgets/argumentParser.tsx
 create mode 100644 src/editor/codemirror/helper-widgets/widgetArgParser.tsx

diff --git a/src/editor/codemirror/helper-widgets/argumentParser.tsx b/src/editor/codemirror/helper-widgets/argumentParser.tsx
deleted file mode 100644
index 7e4b79f9f..000000000
--- a/src/editor/codemirror/helper-widgets/argumentParser.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { EditorState } from "@codemirror/state";
-import { SyntaxNode } from "@lezer/common";
-// TODO: might move parsing to here once arguments are no longer literals
-// see index.d.ts
-
-// Pre: args are all of type number
-export function numberArgs(state: EditorState, args: any[], node:SyntaxNode): number[] {
-    let nums = []
-    args.forEach(function (value) {
-
-    });
-    return []
-}
\ No newline at end of file
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 7b673cbc8..e2a6aba92 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -10,13 +10,15 @@ import { PortalFactory } from "../CodeMirror";
 import React from "react";
 import { MicrobitSinglePixelComponent } from "./setPixelWidget";
 import { MicrobitMultiplePixelComponent } from "./showImageWidget"
-//import { numberArgs } from "./argumentParser";
+import { createWidget } from "./widgetArgParser";
 
-export interface WidgetProps<T> {
+export interface WidgetProps {
   // Note: always an array, can be singleton
-  args: T[]
+  args: any[]
   // Ranges of where to insert each argument
   ranges: {from:number, to:number} []
+  // Whether or not each argument is a literal
+  literals: boolean[]
   // Where to insert the changed values
   from: number,
   to: number
@@ -26,11 +28,11 @@ export interface WidgetProps<T> {
  * This widget will have its contents rendered by the code in CodeMirror.tsx
  * which it communicates with via the portal factory.
  */
-class Widget<T> extends WidgetType {
+class Widget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
   constructor(private component: React.ComponentType<any>,
-    private props:WidgetProps<T>,
+    private props:WidgetProps,
     private createPortal: PortalFactory) {
     super();
   }
@@ -59,54 +61,20 @@ export const reactWidgetExtension = (
 ): Extension => {
   const decorate = (state: EditorState) => {
     let widgets: any[] = []
-    // Creates a widget which accepts arguments of type T
-    function createWidget<T>(comp: React.ComponentType<any>, args: T[], from: number, to: number) {
-      args.forEach(function (value) { console.log(value); })
-      let props = {
-        args: args,
-        ranges: [],
-        from: from,
-        to: to,
-      }
-
-      let deco = Decoration.widget({
-        widget: new Widget<T>(comp, props, createPortal),
-        side: 1,
-      });
-
-      widgets.push(deco.range(to));
-    }
 
     syntaxTree(state).iterate({
       enter: (ref) => {
         // Found an ArgList, parent will be a CallExpression
-        if (ref.name === "ArgList" && ref.node.parent) {
-          //console.log(state.doc.sliceString(ref.node.parent.from, ref.from));
-
+        if (ref.name === "ArgList" && ref.node.parent) {          
           // Match CallExpression name to our widgets
-          switch (state.doc.sliceString(ref.node.parent.from, ref.from)) {
-            case "display.set_pixel":
-              // TODO: assuming all literals for now, will probably want a way to detect other types of arguments
-              let args: number[] = [];
-              ref.node.getChildren("Number").forEach(function (child) { args.push(+state.doc.sliceString(child.from, child.to)) });
-
-              createWidget<number>(MicrobitSinglePixelComponent, args, ref.from, ref.to);
-              break;
-            case "Image":
-              // TODO: does not handle comments properly
-              let imArg: string[] = []
-              let arg = ref.node.getChild("ContinuedString");
-              if (arg) imArg.push(state.doc.sliceString(arg.from, arg.to).replaceAll(/[' \n]/g, ""));
-              else {
-                arg = ref.node.getChild("String");
-                if (arg) imArg.push()
-              }
-
-              createWidget<string>(MicrobitMultiplePixelComponent, imArg, ref.from, ref.to);
-              break;
-            default:
-              // No widget implemented for this function
-              break;
+          let name = state.doc.sliceString(ref.node.parent.from, ref.from)
+          let widget = createWidget(name, state, ref.node);
+          if(widget) {
+            let deco = Decoration.widget({
+              widget: new Widget(widget.comp, widget.props, createPortal),
+              side: 1,
+            });
+            widgets.push(deco.range(ref.to));
           }
         }
       }
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
new file mode 100644
index 000000000..6f343e567
--- /dev/null
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -0,0 +1,72 @@
+import { EditorState } from "@codemirror/state";
+import {
+    Decoration,
+    DecorationSet,
+    EditorView,
+    WidgetType,
+} from "@codemirror/view";
+import { SyntaxNode } from "@lezer/common";
+import { WidgetProps } from "./reactWidgetExtension";
+import { MicrobitSinglePixelComponent } from "./setPixelWidget";
+import { MicrobitMultiplePixelComponent } from "./showImageWidget";
+
+export interface CompProps {
+    comp: React.ComponentType<any>,
+    props: WidgetProps
+}
+
+export function createWidget(name: string, state: EditorState, node: SyntaxNode): CompProps | null {
+    switch (name) {
+        case "display.set_pixel":
+            return {
+                comp: MicrobitSinglePixelComponent,
+                props: {
+                    args: [],
+                    ranges: [],
+                    literals: [],
+                    from: 0,
+                    to: 0
+                }
+            }
+        //     // TODO: assuming all literals for now, will probably want a way to detect other types of arguments
+        //   let args: number[] = [];
+        //   ref.node.getChildren("Number").forEach(function (child) { args.push(+state.doc.sliceString(child.from, child.to)) });
+
+        //   createWidget<number>(MicrobitSinglePixelComponent, args, ref.from, ref.to);
+        case "Image":
+            return {
+                comp: MicrobitMultiplePixelComponent,
+                props: {
+                    args: [],
+                    ranges: [],
+                    literals: [],
+                    from: 0,
+                    to: 0
+                }
+            }
+        
+        // TODO: does not handle comments properly
+        //   let imArg: string[] = []
+        //   let arg = ref.node.getChild("ContinuedString");
+        //   if (arg) imArg.push(state.doc.sliceString(arg.from, arg.to).replaceAll(/[' \n]/g, ""));
+        //   else {
+        //     arg = ref.node.getChild("String");
+        //     if (arg) imArg.push()
+        //   }
+
+        //   createWidget<string>(MicrobitMultiplePixelComponent, imArg, ref.from, ref.to);
+        //   break;
+        default:
+          // No widget implemented for this function
+          return null;
+    }
+}
+
+// Pre: args are all of type number
+export function numberArgs(state: EditorState, args: any[], node:SyntaxNode): number[] {
+    let nums = []
+    args.forEach(function (value) {
+
+    });
+    return []
+}
\ No newline at end of file

From 56b1afa5b567c02a95703f6644266be2f7230858 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sat, 30 Mar 2024 14:27:50 -0400
Subject: [PATCH 053/106] from to

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 6f343e567..e78084afc 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -24,8 +24,8 @@ export function createWidget(name: string, state: EditorState, node: SyntaxNode)
                     args: [],
                     ranges: [],
                     literals: [],
-                    from: 0,
-                    to: 0
+                    from: node.from,
+                    to: node.to
                 }
             }
         //     // TODO: assuming all literals for now, will probably want a way to detect other types of arguments
@@ -40,8 +40,8 @@ export function createWidget(name: string, state: EditorState, node: SyntaxNode)
                     args: [],
                     ranges: [],
                     literals: [],
-                    from: 0,
-                    to: 0
+                    from: node.from,
+                    to: node.to
                 }
             }
         

From 33341fadb51c29f00c508568d5403add7b85eaaa Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 2 Apr 2024 15:54:10 +0200
Subject: [PATCH 054/106] .

---
 src/editor/codemirror/helper-widgets/setPixelWidget.tsx  | 2 +-
 src/editor/codemirror/helper-widgets/showImageWidget.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 76174186e..620a92e94 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -84,7 +84,7 @@ const validateArgs = (args : number[]) => {
 };
 
 
-export const MicrobitSinglePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
+export const MicrobitSinglePixelComponent = ({ args, ranges, literals, from, to }: WidgetProps) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
   if (validateArgs(args)){
     const [x, y, brightness] = parseArgs(args);
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 156851560..22d58acba 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -71,7 +71,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent = ({ args, from, to, view }: WidgetProps<number>) => {
+export const MicrobitMultiplePixelComponent = ({ args, ranges, literals, from, to }: WidgetProps) => {
   let initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
   if (validateArgs(args)) {
     initialSelectedPixels = parseArgs(args)

From 3bb2500ba90e66fe500cb3cf802ed1662ed94ac5 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 2 Apr 2024 09:59:31 -0400
Subject: [PATCH 055/106] open button for widgets

---
 .../helper-widgets/reactWidgetExtension.tsx   | 56 ++++++++++++++++---
 1 file changed, 49 insertions(+), 7 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index e2a6aba92..265688af7 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -1,3 +1,4 @@
+import { Button, HStack, Text } from "@chakra-ui/react";
 import { EditorState, Extension, StateField } from "@codemirror/state";
 import {
   Decoration,
@@ -8,6 +9,7 @@ import {
 import { syntaxTree } from "@codemirror/language"
 import { PortalFactory } from "../CodeMirror";
 import React from "react";
+import { useCallback } from "react";
 import { MicrobitSinglePixelComponent } from "./setPixelWidget";
 import { MicrobitMultiplePixelComponent } from "./showImageWidget"
 import { createWidget } from "./widgetArgParser";
@@ -24,6 +26,27 @@ export interface WidgetProps {
   to: number
 }
 
+// Location of currently open widget, -1 if all closed
+export let openWidgetLoc = -1;
+const OpenReactComponent = ({ loc, view }: { loc: number, view: EditorView }) => {
+  const handleClick = useCallback(() => {
+    openWidgetLoc = loc;
+    // TODO: not sure how to force a view update without a list of changes
+    view.dispatch({
+      changes: {
+        from: 0,
+        to: 1,
+        insert: view.state.doc.sliceString(0, 1),
+      }
+    });
+  }, []);
+  return (
+    <HStack fontFamily="body" spacing={5} py={3}>
+      <Button onClick={handleClick}>Open</Button>
+    </HStack>
+  );
+};
+
 /**
  * This widget will have its contents rendered by the code in CodeMirror.tsx
  * which it communicates with via the portal factory.
@@ -32,7 +55,7 @@ class Widget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
   constructor(private component: React.ComponentType<any>,
-    private props:WidgetProps,
+    private props:WidgetProps, private inline:boolean,
     private createPortal: PortalFactory) {
     super();
   }
@@ -40,7 +63,11 @@ class Widget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
+    if(this.inline) {
+      dom.style.display = 'inline-block'; // want it inline for the open-close widget
+      this.portalCleanup = this.createPortal(dom, <OpenReactComponent loc={this.props.to} view={view} />);
+    }
+    else this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
     return dom;
   }
 
@@ -55,6 +82,7 @@ class Widget extends WidgetType {
   }
 }
 
+
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
@@ -70,11 +98,20 @@ export const reactWidgetExtension = (
           let name = state.doc.sliceString(ref.node.parent.from, ref.from)
           let widget = createWidget(name, state, ref.node);
           if(widget) {
-            let deco = Decoration.widget({
-              widget: new Widget(widget.comp, widget.props, createPortal),
-              side: 1,
-            });
-            widgets.push(deco.range(ref.to));
+            if(widget.props.to == openWidgetLoc){
+              let deco = Decoration.widget({
+                widget: new Widget(widget.comp, widget.props, true, createPortal),
+                side: 1,
+              });
+              widgets.push(deco.range(ref.to));
+            }
+            else{
+              let deco = Decoration.widget({
+                widget: new Widget(widget.comp, widget.props, false, createPortal),
+                side: 1,
+              });
+              widgets.push(deco.range(ref.to));
+            }
           }
         }
       }
@@ -89,6 +126,11 @@ export const reactWidgetExtension = (
     },
     update(widgets, transaction) {
       if (transaction.docChanged) {
+        transaction.changes.iterChangedRanges((_fromA, _toA, _fromB, _toB) => {
+          if(_toA <= openWidgetLoc){
+            openWidgetLoc += (_toB - _fromB) - (_toA - _fromA)
+          }
+        });
         return decorate(transaction.state);
       }
       return widgets.map(transaction.changes);

From 15a997129e49438bacc9213b0386a56c6e9faf1c Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 2 Apr 2024 10:02:33 -0400
Subject: [PATCH 056/106] !=

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 265688af7..2a83755fb 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -98,7 +98,7 @@ export const reactWidgetExtension = (
           let name = state.doc.sliceString(ref.node.parent.from, ref.from)
           let widget = createWidget(name, state, ref.node);
           if(widget) {
-            if(widget.props.to == openWidgetLoc){
+            if(widget.props.to != openWidgetLoc){
               let deco = Decoration.widget({
                 widget: new Widget(widget.comp, widget.props, true, createPortal),
                 side: 1,

From d879e083b7558eaecc90afe3d2a80366f7886d78 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 2 Apr 2024 16:04:27 +0200
Subject: [PATCH 057/106] .

---
 src/editor/codemirror/helper-widgets/setPixelWidget.tsx | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 620a92e94..64daaebb8 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -1,6 +1,9 @@
 import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
 import { useState } from "react";
 import { WidgetProps } from "./reactWidgetExtension";
+import {
+  EditorView,
+} from "@codemirror/view";
 
 interface Pixel {
   x: number;
@@ -84,7 +87,7 @@ const validateArgs = (args : number[]) => {
 };
 
 
-export const MicrobitSinglePixelComponent = ({ args, ranges, literals, from, to }: WidgetProps) => {
+export const MicrobitSinglePixelComponent = ({ args, ranges, literals, from, to }: WidgetProps, view:EditorView) => {
   const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
   if (validateArgs(args)){
     const [x, y, brightness] = parseArgs(args);
@@ -100,14 +103,13 @@ export const MicrobitSinglePixelComponent = ({ args, ranges, literals, from, to
     if (selectedPixel !== null) {
       const { x, y, brightness } = selectedPixel;
       console.log(`(${x}, ${y}, ${brightness}) `);
-      /*view.dispatch({
+      view.dispatch({
         changes: {
           from: from,
           to: to,
           insert: `(${x}, ${y}, ${brightness}) `,
         }
       });
-      */
     }
   };
 

From f2b9ccf146f6fc73adcca1364466117ec8995976 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 2 Apr 2024 16:12:31 +0200
Subject: [PATCH 058/106] state

---
 .../helper-widgets/setPixelWidget.tsx         | 112 +++++++++---------
 1 file changed, 59 insertions(+), 53 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 64daaebb8..6da193f48 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -1,9 +1,16 @@
-import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
-import { useState } from "react";
-import { WidgetProps } from "./reactWidgetExtension";
+
+import {
+  Box,
+  Button,
+  Slider,
+  SliderTrack,
+  SliderFilledTrack,
+  SliderThumb,
+} from "@chakra-ui/react";
 import {
   EditorView,
 } from "@codemirror/view";
+import { WidgetProps } from "./reactWidgetExtension";
 
 interface Pixel {
   x: number;
@@ -13,43 +20,48 @@ interface Pixel {
 
 interface MicrobitSinglePixelGridProps {
   onPixelClick: (pixel: Pixel) => void;
-  initialPixel : Pixel | null;
+  initialPixel: Pixel | null;
 }
 
-const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPixelClick, initialPixel }) => {
-  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(initialPixel);
-  const [currentBrightness, setCurrentBrightness] = useState<number>(5);
-
+const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
+  onPixelClick,
+  initialPixel,
+}) => {
+  const { x, y, brightness } = initialPixel ?? { x: 0, y: 0, brightness: 9 };
   const handlePixelClick = (x: number, y: number) => {
-    const newPixel: Pixel = { x: x, y: y, brightness: currentBrightness };
-    setSelectedPixel(newPixel);
+    const newPixel: Pixel = { x, y, brightness };
     onPixelClick(newPixel);
   };
-
   const handleSliderChange = (value: number) => {
-    setCurrentBrightness(value);
-    if (selectedPixel) {
-      const updatedPixel: Pixel = { ...selectedPixel, brightness: value };
-      onPixelClick(updatedPixel);
-    }
+    const updatedPixel: Pixel = { x, y, brightness: value };
+    onPixelClick(updatedPixel);
   };
 
   return (
     <Box display="flex" flexDirection="row" justifyContent="flex-start">
       <Box>
         <Box bg="black" p="10px" borderRadius="5px">
-          {[...Array(5)].map((_, y) => (
+          {[...Array(5)].map((_, gridY) => (
             <Box key={y} display="flex">
-              {[...Array(5)].map((_, x) => (
+              {[...Array(5)].map((_, gridX) => (
                 <Box key={x} display="flex" mr="2px">
                   <Button
                     size="xs"
                     h="15px"
                     w="15px"
                     p={0}
-                    bgColor={selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${currentBrightness / 9})` : "rgba(255, 255, 255, 0)"}
-                    _hover={{ bgColor: selectedPixel?.x === x && selectedPixel.y === y ? `rgba(255, 0, 0, ${currentBrightness / 9})` : "rgba(255, 255, 255, 0.5)" }}
-                    onClick={() => handlePixelClick(x, y)}
+                    bgColor={
+                      gridX === x && gridY === y
+                        ? `rgba(255, 0, 0, ${brightness / 9})`
+                        : "rgba(255, 255, 255, 0)"
+                    }
+                    _hover={{
+                      bgColor:
+                        gridX === x && gridY === y
+                          ? `rgba(255, 0, 0, ${brightness / 9})`
+                          : "rgba(255, 255, 255, 0.5)",
+                    }}
+                    onClick={() => handlePixelClick(gridX, gridY)}
                   />
                 </Box>
               ))}
@@ -60,14 +72,15 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPix
       <Box ml="10px">
         <Slider
           aria-label="brightness"
-          defaultValue={currentBrightness}
+          defaultValue={brightness}
           min={0}
           max={9}
           step={1}
           orientation="vertical"
           _focus={{ boxShadow: "none" }}
           _active={{ bgColor: "transparent" }}
-          onChange={handleSliderChange}>
+          onChange={handleSliderChange}
+        >
           <SliderTrack>
             <SliderFilledTrack />
           </SliderTrack>
@@ -78,40 +91,33 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({ onPix
   );
 };
 
-const parseArgs = (args : number[]) => {
-  return args
-};
-
-const validateArgs = (args : number[]) => {
-  return Array.isArray(args) && args.length === 3
+const parseArgs = (args: number[]): Pixel | null => {
+  if (Array.isArray(args) && args.length === 3) {
+    const [x, y, brightness] = args;
+    return { x, y, brightness };
+  };
+  return {x:1, y:1, brightness:1};
 };
 
-
-export const MicrobitSinglePixelComponent = ({ args, ranges, literals, from, to }: WidgetProps, view:EditorView) => {
-  const [selectedPixel, setSelectedPixel] = useState<Pixel | null>(null);
-  if (validateArgs(args)){
-    const [x, y, brightness] = parseArgs(args);
-    setSelectedPixel({ x, y, brightness });
-  } 
+export const MicrobitSinglePixelComponent  = ({ args, ranges, literals, from, to }: WidgetProps, view:EditorView) => {
+  const selectedPixel = parseArgs(args);
 
   const handleSelectPixel = (pixel: Pixel) => {
-    setSelectedPixel(pixel);
-    updateView();
+    const { x, y, brightness } = pixel;
+    console.log(`(${x}, ${y}, ${brightness}) `);
+    view.dispatch({
+      changes: {
+        from: from,
+        to: to,
+        insert: `(${x}, ${y}, ${brightness}) `,
+      },
+    });
   };
 
-  const updateView = () => {
-    if (selectedPixel !== null) {
-      const { x, y, brightness } = selectedPixel;
-      console.log(`(${x}, ${y}, ${brightness}) `);
-      view.dispatch({
-        changes: {
-          from: from,
-          to: to,
-          insert: `(${x}, ${y}, ${brightness}) `,
-        }
-      });
-    }
-  };
-
-  return (<MicrobitSinglePixelGrid onPixelClick={handleSelectPixel} initialPixel={selectedPixel} />);
+  return (
+    <MicrobitSinglePixelGrid
+      onPixelClick={handleSelectPixel}
+      initialPixel={selectedPixel}
+    />
+  );
 };

From 4cbdaf14788b4d85d69471f2b30536a0bb68e05b Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 2 Apr 2024 16:15:34 +0200
Subject: [PATCH 059/106] .

---
 .../codemirror/helper-widgets/showImageWidget.tsx      | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 22d58acba..03658f3a2 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -1,6 +1,9 @@
 import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
 import React, { useState } from "react";
 import { WidgetProps } from "./reactWidgetExtension";
+import {
+  EditorView,
+} from "@codemirror/view";
 
 interface MultiMicrobitGridProps {
   selectedPixels: number[][];
@@ -71,7 +74,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent = ({ args, ranges, literals, from, to }: WidgetProps) => {
+export const MicrobitMultiplePixelComponent  = ({ args, ranges, literals, from, to }: WidgetProps, view:EditorView) => {
   let initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
   if (validateArgs(args)) {
     initialSelectedPixels = parseArgs(args)
@@ -88,13 +91,12 @@ export const MicrobitMultiplePixelComponent = ({ args, ranges, literals, from, t
   const updateView = () => {
     let insertion = pixelsToString(selectedPixels);
     console.log(insertion);
-    /*view.dispatch({
+    view.dispatch({
       changes: {
         from: from,
         to: to,
         insert: insertion}
-      });
-    */
+    });
   }
 
   return (

From 8b1d34e3c62e43e71dcec86b8d0a5360e13a3ae6 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 2 Apr 2024 19:07:04 +0200
Subject: [PATCH 060/106] added close button

---
 .../helper-widgets/setPixelWidget.tsx         | 37 ++++++++++++++-----
 .../helper-widgets/showImageWidget.tsx        | 16 +++++++-
 2 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 6da193f48..ff6e67b68 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -1,4 +1,4 @@
-
+import React from "react";
 import {
   Box,
   Button,
@@ -7,9 +7,7 @@ import {
   SliderFilledTrack,
   SliderThumb,
 } from "@chakra-ui/react";
-import {
-  EditorView,
-} from "@codemirror/view";
+import { EditorView } from "@codemirror/view";
 import { WidgetProps } from "./reactWidgetExtension";
 
 interface Pixel {
@@ -21,11 +19,13 @@ interface Pixel {
 interface MicrobitSinglePixelGridProps {
   onPixelClick: (pixel: Pixel) => void;
   initialPixel: Pixel | null;
+  onCloseClick: () => void;
 }
 
 const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   onPixelClick,
   initialPixel,
+  onCloseClick,
 }) => {
   const { x, y, brightness } = initialPixel ?? { x: 0, y: 0, brightness: 9 };
   const handlePixelClick = (x: number, y: number) => {
@@ -39,8 +39,18 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
 
   return (
     <Box display="flex" flexDirection="row" justifyContent="flex-start">
+      <Box ml="10px" style={{ marginRight: "4px" }}>
+        <Button size="xs" onClick={onCloseClick}>
+          X
+        </Button>
+      </Box>
       <Box>
-        <Box bg="black" p="10px" borderRadius="5px">
+        <Box
+          bg="black"
+          p="10px"
+          borderRadius="5px"
+          style={{ marginTop: "15px" }}
+        >
           {[...Array(5)].map((_, gridY) => (
             <Box key={y} display="flex">
               {[...Array(5)].map((_, gridX) => (
@@ -69,7 +79,7 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
           ))}
         </Box>
       </Box>
-      <Box ml="10px">
+      <Box ml="10px" style={{ marginTop: "15px" }}>
         <Slider
           aria-label="brightness"
           defaultValue={brightness}
@@ -95,15 +105,23 @@ const parseArgs = (args: number[]): Pixel | null => {
   if (Array.isArray(args) && args.length === 3) {
     const [x, y, brightness] = args;
     return { x, y, brightness };
-  };
-  return {x:1, y:1, brightness:1};
+  }
+  return { x: 1, y: 1, brightness: 1 };
 };
 
-export const MicrobitSinglePixelComponent  = ({ args, ranges, literals, from, to }: WidgetProps, view:EditorView) => {
+export const MicrobitSinglePixelComponent = (
+  { args, ranges, literals, from, to }: WidgetProps,
+  view: EditorView
+) => {
   const selectedPixel = parseArgs(args);
 
+  const handleCloseClick = () => {
+    console.log("closed");
+  };
+
   const handleSelectPixel = (pixel: Pixel) => {
     const { x, y, brightness } = pixel;
+    console.log("ye" + view.inView);
     console.log(`(${x}, ${y}, ${brightness}) `);
     view.dispatch({
       changes: {
@@ -118,6 +136,7 @@ export const MicrobitSinglePixelComponent  = ({ args, ranges, literals, from, to
     <MicrobitSinglePixelGrid
       onPixelClick={handleSelectPixel}
       initialPixel={selectedPixel}
+      onCloseClick={handleCloseClick}
     />
   );
 };
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 03658f3a2..58d60bc3f 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -7,11 +7,13 @@ import {
 
 interface MultiMicrobitGridProps {
   selectedPixels: number[][];
+  onCloseClick: () => void;
   onPixelChange: (x: number, y: number, brightness: number) => void;
 }
 
 const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   selectedPixels,
+  onCloseClick,
   onPixelChange,
 }) => {
   const [currentBrightness, setCurrentBrightness] = useState<number>(5);
@@ -31,8 +33,13 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
 
   return (
     <Box display="flex" flexDirection="row" justifyContent="flex-start">
+            <Box ml="10px"  style={{ marginRight: '4px' }}>
+        <Button size="xs" onClick={onCloseClick}>
+          X
+        </Button>
+      </Box>
       <Box>
-        <Box bg="black" p="10px" borderRadius="5px">
+        <Box bg="black" p="10px" borderRadius="5px" style={{ marginTop: '15px' }}>
           {selectedPixels.map((row, y) => (
             <Box key={y} display="flex">
               {row.map((brightness, x) => (
@@ -52,7 +59,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
           ))}
         </Box>
       </Box>
-      <Box ml="10px">
+      <Box ml="10px" style={{ marginTop: '15px' }}>
         <Slider
           aria-label="brightness"
           value={currentBrightness}
@@ -99,10 +106,15 @@ export const MicrobitMultiplePixelComponent  = ({ args, ranges, literals, from,
     });
   }
 
+  const handleCloseClick = () => {
+    console.log("closed");
+  };
+
   return (
     <MicrobitMultiplePixelsGrid
       selectedPixels={selectedPixels}
       onPixelChange={handlePixelChange}
+      onCloseClick={handleCloseClick}
     />
   );
 };

From 5e9f6cdbff36d6c24dc2dd6dcc1fbab99e122526 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 7 Apr 2024 10:39:52 -0400
Subject: [PATCH 061/106] Better argument parsing + fixed warnings

- properly pass arguments
- passes ranges for insertion
---
 .../helper-widgets/reactWidgetExtension.tsx   |  29 ++---
 .../helper-widgets/setPixelWidget.tsx         |   2 +-
 .../helper-widgets/showImageWidget.tsx        |   2 +-
 .../helper-widgets/widgetArgParser.tsx        | 107 ++++++++++--------
 4 files changed, 69 insertions(+), 71 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 2a83755fb..81f1933d3 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -1,4 +1,4 @@
-import { Button, HStack, Text } from "@chakra-ui/react";
+import { Button, HStack } from "@chakra-ui/react";
 import { EditorState, Extension, StateField } from "@codemirror/state";
 import {
   Decoration,
@@ -10,8 +10,6 @@ import { syntaxTree } from "@codemirror/language"
 import { PortalFactory } from "../CodeMirror";
 import React from "react";
 import { useCallback } from "react";
-import { MicrobitSinglePixelComponent } from "./setPixelWidget";
-import { MicrobitMultiplePixelComponent } from "./showImageWidget"
 import { createWidget } from "./widgetArgParser";
 
 export interface WidgetProps {
@@ -19,8 +17,8 @@ export interface WidgetProps {
   args: any[]
   // Ranges of where to insert each argument
   ranges: {from:number, to:number} []
-  // Whether or not each argument is a literal
-  literals: boolean[]
+  // Type of each argument, can be checked in widget to determine if it is editable
+  types: string[]
   // Where to insert the changed values
   from: number,
   to: number
@@ -39,7 +37,7 @@ const OpenReactComponent = ({ loc, view }: { loc: number, view: EditorView }) =>
         insert: view.state.doc.sliceString(0, 1),
       }
     });
-  }, []);
+  }, [loc, view]);
   return (
     <HStack fontFamily="body" spacing={5} py={3}>
       <Button onClick={handleClick}>Open</Button>
@@ -98,20 +96,11 @@ export const reactWidgetExtension = (
           let name = state.doc.sliceString(ref.node.parent.from, ref.from)
           let widget = createWidget(name, state, ref.node);
           if(widget) {
-            if(widget.props.to != openWidgetLoc){
-              let deco = Decoration.widget({
-                widget: new Widget(widget.comp, widget.props, true, createPortal),
-                side: 1,
-              });
-              widgets.push(deco.range(ref.to));
-            }
-            else{
-              let deco = Decoration.widget({
-                widget: new Widget(widget.comp, widget.props, false, createPortal),
-                side: 1,
-              });
-              widgets.push(deco.range(ref.to));
-            }
+            let deco = Decoration.widget({
+              widget: new Widget(widget.comp, widget.props, widget.props.to !== openWidgetLoc, createPortal),
+              side: 1,
+            });
+            widgets.push(deco.range(ref.to));
           }
         }
       }
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index ff6e67b68..5f5a110e6 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -110,7 +110,7 @@ const parseArgs = (args: number[]): Pixel | null => {
 };
 
 export const MicrobitSinglePixelComponent = (
-  { args, ranges, literals, from, to }: WidgetProps,
+  { args, ranges, types, from, to }: WidgetProps,
   view: EditorView
 ) => {
   const selectedPixel = parseArgs(args);
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 58d60bc3f..65c698ca1 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -81,7 +81,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent  = ({ args, ranges, literals, from, to }: WidgetProps, view:EditorView) => {
+export const MicrobitMultiplePixelComponent  = ({ args, ranges, types, from, to }: WidgetProps, view:EditorView) => {
   let initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
   if (validateArgs(args)) {
     initialSelectedPixels = parseArgs(args)
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index e78084afc..e0ba7ef18 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -1,10 +1,4 @@
 import { EditorState } from "@codemirror/state";
-import {
-    Decoration,
-    DecorationSet,
-    EditorView,
-    WidgetType,
-} from "@codemirror/view";
 import { SyntaxNode } from "@lezer/common";
 import { WidgetProps } from "./reactWidgetExtension";
 import { MicrobitSinglePixelComponent } from "./setPixelWidget";
@@ -16,57 +10,72 @@ export interface CompProps {
 }
 
 export function createWidget(name: string, state: EditorState, node: SyntaxNode): CompProps | null {
+    let children = getChildNodes(node);  
+    let ranges = getRanges(children);
+    let args = getArgs(state, ranges);
+    let types = getTypes(children);
+    let component: React.ComponentType<any> | null = null 
+
     switch (name) {
         case "display.set_pixel":
-            return {
-                comp: MicrobitSinglePixelComponent,
-                props: {
-                    args: [],
-                    ranges: [],
-                    literals: [],
-                    from: node.from,
-                    to: node.to
-                }
-            }
-        //     // TODO: assuming all literals for now, will probably want a way to detect other types of arguments
-        //   let args: number[] = [];
-        //   ref.node.getChildren("Number").forEach(function (child) { args.push(+state.doc.sliceString(child.from, child.to)) });
-
-        //   createWidget<number>(MicrobitSinglePixelComponent, args, ref.from, ref.to);
+            component = MicrobitSinglePixelComponent;
+            break;
         case "Image":
-            return {
-                comp: MicrobitMultiplePixelComponent,
-                props: {
-                    args: [],
-                    ranges: [],
-                    literals: [],
-                    from: node.from,
-                    to: node.to
-                }
-            }
-        
-        // TODO: does not handle comments properly
-        //   let imArg: string[] = []
-        //   let arg = ref.node.getChild("ContinuedString");
-        //   if (arg) imArg.push(state.doc.sliceString(arg.from, arg.to).replaceAll(/[' \n]/g, ""));
-        //   else {
-        //     arg = ref.node.getChild("String");
-        //     if (arg) imArg.push()
-        //   }
-
-        //   createWidget<string>(MicrobitMultiplePixelComponent, imArg, ref.from, ref.to);
-        //   break;
+            component = MicrobitMultiplePixelComponent;
+            break;
         default:
           // No widget implemented for this function
           return null;
     }
+    if(component){
+        return {
+            comp: component,
+            props: {
+                args: args,
+                ranges: ranges,
+                types: types,
+                from: node.from,
+                to: node.to
+            }
+        }
+    }
+    return null;
 }
 
-// Pre: args are all of type number
-export function numberArgs(state: EditorState, args: any[], node:SyntaxNode): number[] {
-    let nums = []
-    args.forEach(function (value) {
+// Gets all child nodes of a CallExpression, no typechecking
+function getChildNodes(node:SyntaxNode): SyntaxNode[] {
+    let child = node.firstChild?.nextSibling;
+    let children = []
+    while(child && child.name !== ")"){
+        if(child.name !== ",") children.push(child); 
+        child = child.nextSibling;
+    }
+    return children;
+}
+
+// Gets ranges for insertion into arguments
+function getRanges(nodes:SyntaxNode[]):{from:number, to:number}[] {
+    let ranges: {from:number, to:number}[] = []
+    nodes.forEach(function(value) {
+        ranges.push({from:value.from, to:value.to})
+    })
+    return ranges;
+}
+
+// Gets arguments as string
+function getArgs(state:EditorState, ranges:{from:number, to:number}[]): string[] {
+    let args: string[] = []
+    ranges.forEach(function(value) {
+        args.push(state.doc.sliceString(value.from, value.to));
+    })
+    return args;
+}
 
-    });
-    return []
+// Gets types of each arg to determine if it is editable
+function getTypes(nodes:SyntaxNode[]):string[] {
+    let types: string[] = []
+    nodes.forEach(function(value) {
+        types.push(value.name)
+    })
+    return types;
 }
\ No newline at end of file

From 94ab44737499d83e3b2d1e3c8d8a861124d71268 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 7 Apr 2024 12:39:17 -0400
Subject: [PATCH 062/106] open/close now uses effects

---
 .../codemirror/helper-widgets/openWidgets.tsx | 18 ++++++++++
 .../helper-widgets/reactWidgetExtension.tsx   | 35 +++++++------------
 2 files changed, 31 insertions(+), 22 deletions(-)
 create mode 100644 src/editor/codemirror/helper-widgets/openWidgets.tsx

diff --git a/src/editor/codemirror/helper-widgets/openWidgets.tsx b/src/editor/codemirror/helper-widgets/openWidgets.tsx
new file mode 100644
index 000000000..fadb802aa
--- /dev/null
+++ b/src/editor/codemirror/helper-widgets/openWidgets.tsx
@@ -0,0 +1,18 @@
+import { Button, HStack } from "@chakra-ui/react";
+import { StateEffect } from "@codemirror/state";
+import { EditorView } from "@codemirror/view";
+import { useCallback } from "react";
+
+export const openWidgetEffect = StateEffect.define<number>();
+export const OpenReactComponent = ({ loc, view }: { loc: number, view: EditorView }) => {
+  const handleClick = useCallback(() => {
+    view.dispatch({
+      effects: [openWidgetEffect.of(loc)],
+    });
+  }, [loc, view]);
+  return (
+    <HStack fontFamily="body" spacing={5} py={3}>
+      <Button onClick={handleClick}>Open</Button>
+    </HStack>
+  );
+};
\ No newline at end of file
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 81f1933d3..55c132cea 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -1,4 +1,3 @@
-import { Button, HStack } from "@chakra-ui/react";
 import { EditorState, Extension, StateField } from "@codemirror/state";
 import {
   Decoration,
@@ -9,8 +8,8 @@ import {
 import { syntaxTree } from "@codemirror/language"
 import { PortalFactory } from "../CodeMirror";
 import React from "react";
-import { useCallback } from "react";
 import { createWidget } from "./widgetArgParser";
+import { OpenReactComponent, openWidgetEffect } from "./openWidgets";
 
 export interface WidgetProps {
   // Note: always an array, can be singleton
@@ -24,26 +23,7 @@ export interface WidgetProps {
   to: number
 }
 
-// Location of currently open widget, -1 if all closed
-export let openWidgetLoc = -1;
-const OpenReactComponent = ({ loc, view }: { loc: number, view: EditorView }) => {
-  const handleClick = useCallback(() => {
-    openWidgetLoc = loc;
-    // TODO: not sure how to force a view update without a list of changes
-    view.dispatch({
-      changes: {
-        from: 0,
-        to: 1,
-        insert: view.state.doc.sliceString(0, 1),
-      }
-    });
-  }, [loc, view]);
-  return (
-    <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleClick}>Open</Button>
-    </HStack>
-  );
-};
+
 
 /**
  * This widget will have its contents rendered by the code in CodeMirror.tsx
@@ -109,12 +89,23 @@ export const reactWidgetExtension = (
     return Decoration.set(widgets)
   };
 
+  let openWidgetLoc = -1;
   const stateField = StateField.define<DecorationSet>({
     create(state) {
       return decorate(state);
     },
     update(widgets, transaction) {
+      // check for open/close button pressed
+      for (let effect of transaction.effects) {
+        if (effect.is(openWidgetEffect)) {
+          openWidgetLoc = effect.value;
+          return decorate(transaction.state);
+        }
+      }
+      // else check for other doc edits
       if (transaction.docChanged) {
+        // update openWidgetLoc if changes moves it
+        // transaction.changes.mapPos()
         transaction.changes.iterChangedRanges((_fromA, _toA, _fromB, _toB) => {
           if(_toA <= openWidgetLoc){
             openWidgetLoc += (_toB - _fromB) - (_toA - _fromA)

From 137bd10d7a273b48da675a4da945121fb5190559 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 7 Apr 2024 12:56:09 -0400
Subject: [PATCH 063/106] view issue fixed, close button added

---
 .../helper-widgets/reactWidgetExtension.tsx           |  3 ++-
 .../codemirror/helper-widgets/setPixelWidget.tsx      | 10 +++++++---
 .../codemirror/helper-widgets/showImageWidget.tsx     | 11 +++++++++--
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 55c132cea..49aa2c097 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -45,7 +45,8 @@ class Widget extends WidgetType {
       dom.style.display = 'inline-block'; // want it inline for the open-close widget
       this.portalCleanup = this.createPortal(dom, <OpenReactComponent loc={this.props.to} view={view} />);
     }
-    else this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
+    else this.portalCleanup = this.createPortal(dom, <this.component props={this.props} view={view} />);
+    //else this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
     return dom;
   }
 
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 5f5a110e6..717ebbf9b 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -9,6 +9,7 @@ import {
 } from "@chakra-ui/react";
 import { EditorView } from "@codemirror/view";
 import { WidgetProps } from "./reactWidgetExtension";
+import { openWidgetEffect } from "./openWidgets";
 
 interface Pixel {
   x: number;
@@ -110,13 +111,16 @@ const parseArgs = (args: number[]): Pixel | null => {
 };
 
 export const MicrobitSinglePixelComponent = (
-  { args, ranges, types, from, to }: WidgetProps,
-  view: EditorView
+  { props, view } : {props: WidgetProps, view: EditorView}
 ) => {
+  let args = props.args; let ranges = props.ranges; let types = props.types; let from = props.from; let to = props.to;
+  
   const selectedPixel = parseArgs(args);
 
   const handleCloseClick = () => {
-    console.log("closed");
+    view.dispatch({
+      effects: [openWidgetEffect.of(-1)],
+    });
   };
 
   const handleSelectPixel = (pixel: Pixel) => {
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 65c698ca1..97aaef777 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -4,6 +4,7 @@ import { WidgetProps } from "./reactWidgetExtension";
 import {
   EditorView,
 } from "@codemirror/view";
+import { openWidgetEffect } from "./openWidgets";
 
 interface MultiMicrobitGridProps {
   selectedPixels: number[][];
@@ -81,7 +82,11 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent  = ({ args, ranges, types, from, to }: WidgetProps, view:EditorView) => {
+export const MicrobitMultiplePixelComponent  = (
+  { props, view } : {props: WidgetProps, view: EditorView}
+) => {
+  let args = props.args; let ranges = props.ranges; let types = props.types; let from = props.from; let to = props.to;
+
   let initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
   if (validateArgs(args)) {
     initialSelectedPixels = parseArgs(args)
@@ -107,7 +112,9 @@ export const MicrobitMultiplePixelComponent  = ({ args, ranges, types, from, to
   }
 
   const handleCloseClick = () => {
-    console.log("closed");
+    view.dispatch({
+      effects: [openWidgetEffect.of(-1)],
+    });
   };
 
   return (

From 11431aec7dcd705548a7b88bfc2541129f4eb4ec Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Sun, 7 Apr 2024 13:09:06 -0400
Subject: [PATCH 064/106] use mapPos instead of iterChangedRanges

---
 .../codemirror/helper-widgets/reactWidgetExtension.tsx     | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 49aa2c097..ea88e64b6 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -106,12 +106,7 @@ export const reactWidgetExtension = (
       // else check for other doc edits
       if (transaction.docChanged) {
         // update openWidgetLoc if changes moves it
-        // transaction.changes.mapPos()
-        transaction.changes.iterChangedRanges((_fromA, _toA, _fromB, _toB) => {
-          if(_toA <= openWidgetLoc){
-            openWidgetLoc += (_toB - _fromB) - (_toA - _fromA)
-          }
-        });
+        openWidgetLoc = transaction.changes.mapPos(openWidgetLoc);
         return decorate(transaction.state);
       }
       return widgets.map(transaction.changes);

From a5fb3024cc2682744e22ff314b772e03635eae86 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 8 Apr 2024 10:08:10 +0200
Subject: [PATCH 065/106] parsing for setpixel

---
 .../helper-widgets/setPixelWidget.tsx         | 30 +++++++++++++------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 717ebbf9b..ccb82c5f9 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -102,20 +102,34 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   );
 };
 
-const parseArgs = (args: number[]): Pixel | null => {
-  if (Array.isArray(args) && args.length === 3) {
-    const [x, y, brightness] = args;
-    return { x, y, brightness };
+const parseArgs  = (args: string[], types: string[]): Pixel | null => {
+  if (args.length > 3) {
+    return null; // If there are more than 3 arguments, return null
   }
-  return { x: 1, y: 1, brightness: 1 };
+  const parsedArgs: number[] = [];
+  for (let i = 0; i < args.length; i++) {
+    let arg = args[i]
+    if (types[i] === 'Number') {
+        parsedArgs.push(parseInt(arg));
+    } else if (arg == ","){
+      parsedArgs.push(0); // For non-number types, default to 0
+    } else {
+      return null
+    }
+  }
+  while (parsedArgs.length < 3) {
+    parsedArgs.push(0);
+  }
+  return {x: parsedArgs[0], y: parsedArgs[1], brightness : parsedArgs[2]}
 };
 
 export const MicrobitSinglePixelComponent = (
   { props, view } : {props: WidgetProps, view: EditorView}
 ) => {
   let args = props.args; let ranges = props.ranges; let types = props.types; let from = props.from; let to = props.to;
-  
-  const selectedPixel = parseArgs(args);
+  console.log(args)
+  console.log(types)
+  const selectedPixel = parseArgs(args, types);
 
   const handleCloseClick = () => {
     view.dispatch({
@@ -125,8 +139,6 @@ export const MicrobitSinglePixelComponent = (
 
   const handleSelectPixel = (pixel: Pixel) => {
     const { x, y, brightness } = pixel;
-    console.log("ye" + view.inView);
-    console.log(`(${x}, ${y}, ${brightness}) `);
     view.dispatch({
       changes: {
         from: from,

From 6cc60ef8191cb1c31d712de044c5b0cd73d8d10e Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Mon, 8 Apr 2024 12:09:20 +0200
Subject: [PATCH 066/106] .

---
 .../helper-widgets/setPixelWidget.tsx         |  41 ++++---
 .../helper-widgets/showImageWidget.tsx        | 107 +++++++++++++-----
 2 files changed, 104 insertions(+), 44 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index ccb82c5f9..ee701ab9b 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -102,35 +102,46 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   );
 };
 
-const parseArgs  = (args: string[], types: string[]): Pixel | null => {
+const parseArgs = (args: string[], types: string[]): Pixel | null => {
   if (args.length > 3) {
-    return null; // If there are more than 3 arguments, return null
+    return null;
   }
   const parsedArgs: number[] = [];
   for (let i = 0; i < args.length; i++) {
-    let arg = args[i]
-    if (types[i] === 'Number') {
-        parsedArgs.push(parseInt(arg));
-    } else if (arg == ","){
-      parsedArgs.push(0); // For non-number types, default to 0
+    let arg = args[i];
+    if (types[i] === "Number") {
+      parsedArgs.push(parseInt(arg));
+    } else if (arg == ",") {
+      parsedArgs.push(0);
     } else {
-      return null
+      return null;
     }
   }
   while (parsedArgs.length < 3) {
     parsedArgs.push(0);
   }
-  return {x: parsedArgs[0], y: parsedArgs[1], brightness : parsedArgs[2]}
+  return { x: parsedArgs[0], y: parsedArgs[1], brightness: parsedArgs[2] };
 };
 
-export const MicrobitSinglePixelComponent = (
-  { props, view } : {props: WidgetProps, view: EditorView}
-) => {
-  let args = props.args; let ranges = props.ranges; let types = props.types; let from = props.from; let to = props.to;
-  console.log(args)
-  console.log(types)
+export const MicrobitSinglePixelComponent = ({
+  props,
+  view,
+}: {
+  props: WidgetProps;
+  view: EditorView;
+}) => {
+  let args = props.args;
+  let ranges = props.ranges;
+  let types = props.types;
+  let from = props.from;
+  let to = props.to;
+  console.log(args);
+  console.log(types);
   const selectedPixel = parseArgs(args, types);
 
+  if (selectedPixel == null) {
+  }
+
   const handleCloseClick = () => {
     view.dispatch({
       effects: [openWidgetEffect.of(-1)],
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 97aaef777..0238e36c9 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -1,9 +1,14 @@
-import { Box, Button, Slider, SliderTrack, SliderFilledTrack, SliderThumb } from "@chakra-ui/react";
+import {
+  Box,
+  Button,
+  Slider,
+  SliderTrack,
+  SliderFilledTrack,
+  SliderThumb,
+} from "@chakra-ui/react";
 import React, { useState } from "react";
 import { WidgetProps } from "./reactWidgetExtension";
-import {
-  EditorView,
-} from "@codemirror/view";
+import { EditorView } from "@codemirror/view";
 import { openWidgetEffect } from "./openWidgets";
 
 interface MultiMicrobitGridProps {
@@ -18,7 +23,10 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   onPixelChange,
 }) => {
   const [currentBrightness, setCurrentBrightness] = useState<number>(5);
-  const [selectedPixel, setSelectedPixel] = useState<{ x: number; y: number } | null>(null);
+  const [selectedPixel, setSelectedPixel] = useState<{
+    x: number;
+    y: number;
+  } | null>(null);
 
   const handlePixelClick = (x: number, y: number) => {
     setSelectedPixel({ x, y });
@@ -34,13 +42,18 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
 
   return (
     <Box display="flex" flexDirection="row" justifyContent="flex-start">
-            <Box ml="10px"  style={{ marginRight: '4px' }}>
+      <Box ml="10px" style={{ marginRight: "4px" }}>
         <Button size="xs" onClick={onCloseClick}>
           X
         </Button>
       </Box>
       <Box>
-        <Box bg="black" p="10px" borderRadius="5px" style={{ marginTop: '15px' }}>
+        <Box
+          bg="black"
+          p="10px"
+          borderRadius="5px"
+          style={{ marginTop: "15px" }}
+        >
           {selectedPixels.map((row, y) => (
             <Box key={y} display="flex">
               {row.map((brightness, x) => (
@@ -51,7 +64,12 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
                     w="15px"
                     p={0}
                     bgColor={`rgba(255, 0, 0, ${brightness / 9})`}
-                    _hover={{ bgColor: brightness > 0 ? `rgba(255, 100, 100, ${selectedPixels[y][x] / 9})` : "rgba(255, 255, 255, 0.5)" }}
+                    _hover={{
+                      bgColor:
+                        brightness > 0
+                          ? `rgba(255, 100, 100, ${selectedPixels[y][x] / 9})`
+                          : "rgba(255, 255, 255, 0.5)",
+                    }}
                     onClick={() => handlePixelClick(x, y)}
                   />
                 </Box>
@@ -60,7 +78,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
           ))}
         </Box>
       </Box>
-      <Box ml="10px" style={{ marginTop: '15px' }}>
+      <Box ml="10px" style={{ marginTop: "15px" }}>
         <Slider
           aria-label="brightness"
           value={currentBrightness}
@@ -82,16 +100,25 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   );
 };
 
-export const MicrobitMultiplePixelComponent  = (
-  { props, view } : {props: WidgetProps, view: EditorView}
-) => {
-  let args = props.args; let ranges = props.ranges; let types = props.types; let from = props.from; let to = props.to;
+export const MicrobitMultiplePixelComponent = ({
+  props,
+  view,
+}: {
+  props: WidgetProps;
+  view: EditorView;
+}) => {
+  let args = props.args;
+  let ranges = props.ranges;
+  let types = props.types;
+  let from = props.from;
+  let to = props.to;
 
-  let initialSelectedPixels: number[][] = Array.from({ length: 5 }, () => Array(5).fill(0));
-  if (validateArgs(args)) {
-    initialSelectedPixels = parseArgs(args)
-  }
-  const [selectedPixels, setSelectedPixels] = useState<number[][]>(initialSelectedPixels);
+  console.log(args);
+  console.log(types);
+  const initialSelectedPixels = parseArgs(args);
+  const [selectedPixels, setSelectedPixels] = useState<number[][]>(
+    initialSelectedPixels
+  );
 
   const handlePixelChange = (x: number, y: number, brightness: number) => {
     const updatedPixels = [...selectedPixels];
@@ -107,9 +134,10 @@ export const MicrobitMultiplePixelComponent  = (
       changes: {
         from: from,
         to: to,
-        insert: insertion}
+        insert: insertion,
+      },
     });
-  }
+  };
 
   const handleCloseClick = () => {
     view.dispatch({
@@ -126,23 +154,44 @@ export const MicrobitMultiplePixelComponent  = (
   );
 };
 
-const parseArgs = (args: number[]) => {
-  return []
-};
+const parseArgs = (args: string[]): number[][] => {
+  const defaultPixels = Array.from({ length: 5 }, () => Array(5).fill(0));
+  // If args is empty, return a 5x5 array filled with zeros
+  if (args.length === 0) {
+    return defaultPixels;
+  }
 
-const validateArgs = (args: number[]) => {
-  return false
-};
+  if (args.length !== 1) {
+    return defaultPixels;
+  }
+  const argString = args[0];
+  const rows = argString.split(":");
 
+  if (rows.length !== 5) {
+    return defaultPixels;
+  }
+
+  const numbers: number[][] = [];
+  for (let row of rows) {
+    row = row.trim();
+    if (!/^\d{5}$/.test(row)) {
+      return defaultPixels;
+    }
+    const rowNumbers = row.split("").map(Number);
+    numbers.push(rowNumbers);
+  }
+
+  return numbers;
+};
 
 function pixelsToString(pixels: number[][]): string {
-  let outputString = '';
+  let outputString = "";
   for (let y = 0; y < 5; y++) {
     for (let x = 0; x < 5; x++) {
       outputString += pixels[y][x].toString();
     }
-    outputString += ':';
+    outputString += ":";
   }
   outputString = outputString.slice(0, -1);
   return outputString;
-}
\ No newline at end of file
+}

From 9c4e3f38dc5db64051f690c5f1285de74fb73be5 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 10:40:09 +0200
Subject: [PATCH 067/106] lint

---
 .../codemirror/helper-widgets/openWidgets.tsx |  10 +-
 .../helper-widgets/reactWidgetExtension.tsx   |  63 +++++----
 .../helper-widgets/widgetArgParser.tsx        | 125 +++++++++---------
 3 files changed, 111 insertions(+), 87 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/openWidgets.tsx b/src/editor/codemirror/helper-widgets/openWidgets.tsx
index fadb802aa..f8df3e5b4 100644
--- a/src/editor/codemirror/helper-widgets/openWidgets.tsx
+++ b/src/editor/codemirror/helper-widgets/openWidgets.tsx
@@ -4,7 +4,13 @@ import { EditorView } from "@codemirror/view";
 import { useCallback } from "react";
 
 export const openWidgetEffect = StateEffect.define<number>();
-export const OpenReactComponent = ({ loc, view }: { loc: number, view: EditorView }) => {
+export const OpenReactComponent = ({
+  loc,
+  view,
+}: {
+  loc: number;
+  view: EditorView;
+}) => {
   const handleClick = useCallback(() => {
     view.dispatch({
       effects: [openWidgetEffect.of(loc)],
@@ -15,4 +21,4 @@ export const OpenReactComponent = ({ loc, view }: { loc: number, view: EditorVie
       <Button onClick={handleClick}>Open</Button>
     </HStack>
   );
-};
\ No newline at end of file
+};
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index ea88e64b6..4e0d61087 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -5,7 +5,7 @@ import {
   EditorView,
   WidgetType,
 } from "@codemirror/view";
-import { syntaxTree } from "@codemirror/language"
+import { syntaxTree } from "@codemirror/language";
 import { PortalFactory } from "../CodeMirror";
 import React from "react";
 import { createWidget } from "./widgetArgParser";
@@ -13,18 +13,16 @@ import { OpenReactComponent, openWidgetEffect } from "./openWidgets";
 
 export interface WidgetProps {
   // Note: always an array, can be singleton
-  args: any[]
+  args: any[];
   // Ranges of where to insert each argument
-  ranges: {from:number, to:number} []
+  ranges: { from: number; to: number }[];
   // Type of each argument, can be checked in widget to determine if it is editable
-  types: string[]
+  types: string[];
   // Where to insert the changed values
-  from: number,
-  to: number
+  from: number;
+  to: number;
 }
 
-
-
 /**
  * This widget will have its contents rendered by the code in CodeMirror.tsx
  * which it communicates with via the portal factory.
@@ -32,20 +30,29 @@ export interface WidgetProps {
 class Widget extends WidgetType {
   private portalCleanup: (() => void) | undefined;
 
-  constructor(private component: React.ComponentType<any>,
-    private props:WidgetProps, private inline:boolean,
-    private createPortal: PortalFactory) {
+  constructor(
+    private component: React.ComponentType<any>,
+    private props: WidgetProps,
+    private inline: boolean,
+    private createPortal: PortalFactory
+  ) {
     super();
   }
 
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    if(this.inline) {
-      dom.style.display = 'inline-block'; // want it inline for the open-close widget
-      this.portalCleanup = this.createPortal(dom, <OpenReactComponent loc={this.props.to} view={view} />);
-    }
-    else this.portalCleanup = this.createPortal(dom, <this.component props={this.props} view={view} />);
+    if (this.inline) {
+      dom.style.display = "inline-block"; // want it inline for the open-close widget
+      this.portalCleanup = this.createPortal(
+        dom,
+        <OpenReactComponent loc={this.props.to} view={view} />
+      );
+    } else
+      this.portalCleanup = this.createPortal(
+        dom,
+        <this.component props={this.props} view={view} />
+      );
     //else this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
     return dom;
   }
@@ -61,33 +68,37 @@ class Widget extends WidgetType {
   }
 }
 
-
 // Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
 ): Extension => {
   const decorate = (state: EditorState) => {
-    let widgets: any[] = []
+    let widgets: any[] = [];
 
     syntaxTree(state).iterate({
       enter: (ref) => {
         // Found an ArgList, parent will be a CallExpression
-        if (ref.name === "ArgList" && ref.node.parent) {          
+        if (ref.name === "ArgList" && ref.node.parent) {
           // Match CallExpression name to our widgets
-          let name = state.doc.sliceString(ref.node.parent.from, ref.from)
+          let name = state.doc.sliceString(ref.node.parent.from, ref.from);
           let widget = createWidget(name, state, ref.node);
-          if(widget) {
+          if (widget) {
             let deco = Decoration.widget({
-              widget: new Widget(widget.comp, widget.props, widget.props.to !== openWidgetLoc, createPortal),
+              widget: new Widget(
+                widget.comp,
+                widget.props,
+                widget.props.to !== openWidgetLoc,
+                createPortal
+              ),
               side: 1,
             });
             widgets.push(deco.range(ref.to));
           }
         }
-      }
-    })
+      },
+    });
 
-    return Decoration.set(widgets)
+    return Decoration.set(widgets);
   };
 
   let openWidgetLoc = -1;
@@ -116,4 +127,4 @@ export const reactWidgetExtension = (
     },
   });
   return [stateField];
-}
+};
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index e0ba7ef18..3c5b32e85 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -5,77 +5,84 @@ import { MicrobitSinglePixelComponent } from "./setPixelWidget";
 import { MicrobitMultiplePixelComponent } from "./showImageWidget";
 
 export interface CompProps {
-    comp: React.ComponentType<any>,
-    props: WidgetProps
+  comp: React.ComponentType<any>;
+  props: WidgetProps;
 }
 
-export function createWidget(name: string, state: EditorState, node: SyntaxNode): CompProps | null {
-    let children = getChildNodes(node);  
-    let ranges = getRanges(children);
-    let args = getArgs(state, ranges);
-    let types = getTypes(children);
-    let component: React.ComponentType<any> | null = null 
+export function createWidget(
+  name: string,
+  state: EditorState,
+  node: SyntaxNode
+): CompProps | null {
+  let children = getChildNodes(node);
+  let ranges = getRanges(children);
+  let args = getArgs(state, ranges);
+  let types = getTypes(children);
+  let component: React.ComponentType<any> | null = null;
 
-    switch (name) {
-        case "display.set_pixel":
-            component = MicrobitSinglePixelComponent;
-            break;
-        case "Image":
-            component = MicrobitMultiplePixelComponent;
-            break;
-        default:
-          // No widget implemented for this function
-          return null;
-    }
-    if(component){
-        return {
-            comp: component,
-            props: {
-                args: args,
-                ranges: ranges,
-                types: types,
-                from: node.from,
-                to: node.to
-            }
-        }
-    }
-    return null;
+  switch (name) {
+    case "display.set_pixel":
+      component = MicrobitSinglePixelComponent;
+      break;
+    case "Image":
+      component = MicrobitMultiplePixelComponent;
+      break;
+    default:
+      // No widget implemented for this function
+      return null;
+  }
+  if (component) {
+    return {
+      comp: component,
+      props: {
+        args: args,
+        ranges: ranges,
+        types: types,
+        from: node.from,
+        to: node.to,
+      },
+    };
+  }
+  return null;
 }
 
 // Gets all child nodes of a CallExpression, no typechecking
-function getChildNodes(node:SyntaxNode): SyntaxNode[] {
-    let child = node.firstChild?.nextSibling;
-    let children = []
-    while(child && child.name !== ")"){
-        if(child.name !== ",") children.push(child); 
-        child = child.nextSibling;
-    }
-    return children;
+function getChildNodes(node: SyntaxNode): SyntaxNode[] {
+  let child = node.firstChild?.nextSibling;
+  let children = [];
+  while (child && child.name !== ")") {
+    if (child.name !== ",") children.push(child);
+    child = child.nextSibling;
+  }
+  return children;
 }
 
 // Gets ranges for insertion into arguments
-function getRanges(nodes:SyntaxNode[]):{from:number, to:number}[] {
-    let ranges: {from:number, to:number}[] = []
-    nodes.forEach(function(value) {
-        ranges.push({from:value.from, to:value.to})
-    })
-    return ranges;
+function getRanges(nodes: SyntaxNode[]): { from: number; to: number }[] {
+  let ranges: { from: number; to: number }[] = [];
+  nodes.forEach(function (value) {
+    ranges.push({ from: value.from, to: value.to });
+  });
+  return ranges;
 }
 
 // Gets arguments as string
-function getArgs(state:EditorState, ranges:{from:number, to:number}[]): string[] {
-    let args: string[] = []
-    ranges.forEach(function(value) {
-        args.push(state.doc.sliceString(value.from, value.to));
-    })
-    return args;
+function getArgs(
+  state: EditorState,
+  ranges: { from: number; to: number }[]
+): string[] {
+  let args: string[] = [];
+  ranges.forEach(function (value) {
+    args.push(state.doc.sliceString(value.from, value.to));
+  });
+  return args;
 }
 
 // Gets types of each arg to determine if it is editable
-function getTypes(nodes:SyntaxNode[]):string[] {
-    let types: string[] = []
-    nodes.forEach(function(value) {
-        types.push(value.name)
-    })
-    return types;
-}
\ No newline at end of file
+function getTypes(nodes: SyntaxNode[]): string[] {
+  let types: string[] = [];
+  nodes.forEach(function (value) {
+    types.push(value.name);
+  });
+  return types;
+}

From 2747d8bd665a202979e4012e9075d129824824e0 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 10:42:26 +0200
Subject: [PATCH 068/106] kinda fixed image insertion, the open just pops up in
 the wrong place second time

---
 src/editor/codemirror/helper-widgets/showImageWidget.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 0238e36c9..d220d657f 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -185,7 +185,7 @@ const parseArgs = (args: string[]): number[][] => {
 };
 
 function pixelsToString(pixels: number[][]): string {
-  let outputString = "";
+  let outputString = "(";
   for (let y = 0; y < 5; y++) {
     for (let x = 0; x < 5; x++) {
       outputString += pixels[y][x].toString();
@@ -193,5 +193,6 @@ function pixelsToString(pixels: number[][]): string {
     outputString += ":";
   }
   outputString = outputString.slice(0, -1);
+  outputString += ")";
   return outputString;
 }

From 0f9e3199a573b1d4fbaa9a9218a3548dc2d82ab8 Mon Sep 17 00:00:00 2001
From: Marta Religa <59261954+martarel@users.noreply.github.com>
Date: Tue, 9 Apr 2024 09:51:24 +0100
Subject: [PATCH 069/106] Update build.yml

---
 .github/workflows/build.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c74d395cb..baa4d6c17 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -23,6 +23,7 @@ jobs:
       STAGING_CLOUDFRONT_DISTRIBUTION_ID: E2ELTBTA2OFPY2
       REVIEW_CLOUDFRONT_DISTRIBUTION_ID: E3267W09ZJHQG9
       REACT_APP_FOUNDATION_BUILD: ${{ github.repository_owner == 'microbit-foundation' }}
+      CI: false
 
     steps:
       # Note: This workflow disables deployment steps and micro:bit branding installation on forks.

From dd7d6025d3e74d03e4cc3ced29e8b4a155b54891 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 11:09:34 +0200
Subject: [PATCH 070/106] .

---
 src/editor/codemirror/helper-widgets/setPixelWidget.tsx  | 4 ++--
 src/editor/codemirror/helper-widgets/showImageWidget.tsx | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index ee701ab9b..aa8b2749f 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -111,7 +111,7 @@ const parseArgs = (args: string[], types: string[]): Pixel | null => {
     let arg = args[i];
     if (types[i] === "Number") {
       parsedArgs.push(parseInt(arg));
-    } else if (arg == ",") {
+    } else if (arg === ",") {
       parsedArgs.push(0);
     } else {
       return null;
@@ -131,7 +131,7 @@ export const MicrobitSinglePixelComponent = ({
   view: EditorView;
 }) => {
   let args = props.args;
-  let ranges = props.ranges;
+  //let ranges = props.ranges;
   let types = props.types;
   let from = props.from;
   let to = props.to;
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index d220d657f..e4c815adb 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -108,7 +108,7 @@ export const MicrobitMultiplePixelComponent = ({
   view: EditorView;
 }) => {
   let args = props.args;
-  let ranges = props.ranges;
+  //let ranges = props.ranges;
   let types = props.types;
   let from = props.from;
   let to = props.to;

From b83a1a1f1a1c26d5220d7c8348b9b70e248591ce Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 15:28:29 +0200
Subject: [PATCH 071/106] new component yay

---
 .../codemirror/helper-widgets/soundWidget.tsx | 191 ++++++++++++++++++
 .../helper-widgets/widgetArgParser.tsx        |   4 +
 2 files changed, 195 insertions(+)
 create mode 100644 src/editor/codemirror/helper-widgets/soundWidget.tsx

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
new file mode 100644
index 000000000..5dd98d5cf
--- /dev/null
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -0,0 +1,191 @@
+import React, { useState } from "react";
+import {
+  Box,
+  Button,
+  SliderTrack,
+  SliderFilledTrack,
+  SliderThumb,
+  HStack,
+} from "@chakra-ui/react";
+import { EditorView } from "@codemirror/view";
+import { WidgetProps } from "./reactWidgetExtension";
+import { openWidgetEffect } from "./openWidgets";
+
+interface SliderProps {
+  min: number;
+  max: number;
+  step: number;
+  defaultValue: number;
+  onChange: (value: number) => void;
+  sliderStyle?: React.CSSProperties;
+  label: string;
+}
+
+const DurationSliderProps: SliderProps = {
+  min: 0,
+  max: 100,
+  step: 1,
+  defaultValue: 50,
+  onChange: (value) => {
+    console.log("Slider value changed:", value);
+  },
+  sliderStyle: {
+    width: "100%", // Adjust the width of the slider
+    height: "100px", // Adjust the height of the slider
+    backgroundColor: "lightgray", // Change the background color of the slider
+    borderRadius: "10px", // Apply rounded corners to the slider track
+    border: "none", // Remove the border of the slider track
+    outline: "none", // Remove the outline when focused
+  },
+  label: "Duration",
+};
+
+const endSliderProps: SliderProps = {
+  min: 0,
+  max: 100,
+  step: 1,
+  defaultValue: 50,
+  onChange: (value) => {
+    console.log("Slider value changed:", value);
+  },
+  sliderStyle: {
+    width: "100%", // Adjust the width of the slider
+    height: "100px", // Adjust the height of the slider
+    backgroundColor: "lightgray", // Change the background color of the slider
+    borderRadius: "10px", // Apply rounded corners to the slider track
+    border: "none", // Remove the border of the slider track
+    outline: "none", // Remove the outline when focused
+  },
+  label: "End Freq",
+};
+
+const startSliderProps: SliderProps = {
+  min: 0,
+  max: 100,
+  step: 1,
+  defaultValue: 50,
+  onChange: (value) => {
+    console.log("Slider value changed:", value);
+  },
+  sliderStyle: {
+    width: "200%", // Adjust the width of the slider
+    height: "100px", // Adjust the height of the slider
+    backgroundColor: "lightgray", // Change the background color of the slider
+    borderRadius: "10px", // Apply rounded corners to the slider track
+    border: "none", // Remove the border of the slider track
+    outline: "none", // Remove the outline when focused
+  },
+  label: "Start Freq",
+};
+
+const customSliderStyle: React.CSSProperties = {
+  width: "80%", // Adjust the width of the slider
+  height: "20px", // Adjust the height of the slider
+};
+
+const Slider: React.FC<SliderProps> = ({
+  min,
+  max,
+  step,
+  defaultValue,
+  onChange,
+  sliderStyle,
+  label,
+}) => {
+  const [value, setValue] = useState(defaultValue);
+
+  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    const newValue = parseFloat(event.target.value);
+    setValue(newValue);
+    onChange(newValue);
+  };
+
+  return (
+    <div
+      style={{ display: "flex", flexDirection: "column", alignItems: "left" }}
+    >
+      <input
+        type="range"
+        min={min}
+        max={max}
+        step={step}
+        value={value}
+        onChange={handleChange}
+      />
+      <label style={{ fontSize: "16px" }}>
+        {label}: {value}
+      </label>
+    </div>
+  );
+};
+
+const TripleSliderWidget: React.FC<{
+  slider1Props: SliderProps;
+  slider2Props: SliderProps;
+  slider3Props: SliderProps;
+  isOpen: boolean;
+}> = ({ slider1Props, slider2Props, slider3Props, isOpen }) => {
+  if (!isOpen) return null;
+  return (
+    <div>
+      <div style={{ display: "flex", justifyContent: "left" }}>
+        <div style={{ marginRight: "40px" }}>
+          <Slider {...slider1Props} />
+        </div>
+        <div style={{ marginRight: "40px" }}>
+          <Slider {...slider2Props} />
+        </div>
+        <div>
+          <Slider {...slider3Props} />
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export const SoundComponent = ({
+  props,
+  view,
+}: {
+  props: WidgetProps;
+  view: EditorView;
+}) => {
+  let args = props.args;
+  //let ranges = props.ranges;
+  let types = props.types;
+  let from = props.from;
+  let to = props.to;
+  //for future reference add a aclose button
+  const handleCloseClick = () => {
+    view.dispatch({
+      effects: [openWidgetEffect.of(-1)],
+    });
+  };
+  const [isSoundEditorOpen, setIsSoundEditorOpen] = useState(false);
+  const buttonLabel = isSoundEditorOpen ? "Close" : "Open";
+
+  const handleButtonClick = () => {
+    setIsSoundEditorOpen(!isSoundEditorOpen);
+    // Toggle the state to open/close the DualSlider
+  };
+  /*
+  view.dispatch({
+    changes: {
+      from: from,
+      to: to,
+      insert:  //something from state of component`,
+    },
+    */
+
+  return (
+    <HStack fontFamily="body" spacing={5} py={3}>
+      <Button onClick={handleButtonClick}>{buttonLabel} Sound Editor</Button>
+      <TripleSliderWidget
+        slider1Props={startSliderProps}
+        slider2Props={endSliderProps}
+        slider3Props={DurationSliderProps}
+        isOpen={isSoundEditorOpen}
+      />
+    </HStack>
+  );
+};
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 3c5b32e85..16d9d4c2d 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -3,6 +3,7 @@ import { SyntaxNode } from "@lezer/common";
 import { WidgetProps } from "./reactWidgetExtension";
 import { MicrobitSinglePixelComponent } from "./setPixelWidget";
 import { MicrobitMultiplePixelComponent } from "./showImageWidget";
+import { SoundComponent } from "./soundWidget";
 
 export interface CompProps {
   comp: React.ComponentType<any>;
@@ -27,6 +28,9 @@ export function createWidget(
     case "Image":
       component = MicrobitMultiplePixelComponent;
       break;
+    case "SoundEffect":
+      component = SoundComponent;
+      break;
     default:
       // No widget implemented for this function
       return null;

From ba834ec8298c8bf74a9f337d7662bd0fd3002bba Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 09:30:29 -0400
Subject: [PATCH 072/106] soundEffect

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 3c5b32e85..89ab8d4a4 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -25,6 +25,11 @@ export function createWidget(
       component = MicrobitSinglePixelComponent;
       break;
     case "Image":
+      component = MicrobitMultiplePixelComponent;
+      console.log(args);
+      break;
+    case "SoundEffect":
+      // TODO: sound effect
       component = MicrobitMultiplePixelComponent;
       break;
     default:

From a94f7851e5e4515812d17528c35eaf640d1e3ba4 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 09:35:10 -0400
Subject: [PATCH 073/106] consolelog

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 89ab8d4a4..5d584f979 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -26,7 +26,6 @@ export function createWidget(
       break;
     case "Image":
       component = MicrobitMultiplePixelComponent;
-      console.log(args);
       break;
     case "SoundEffect":
       // TODO: sound effect

From f8f1793d383a2f62666c9c9d10f64e13fb49161b Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 09:41:27 -0400
Subject: [PATCH 074/106] validate in reactWdiget

---
 .../helper-widgets/reactWidgetExtension.tsx          | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 4e0d61087..607784c42 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -43,11 +43,13 @@ class Widget extends WidgetType {
     const dom = document.createElement("div");
 
     if (this.inline) {
-      dom.style.display = "inline-block"; // want it inline for the open-close widget
-      this.portalCleanup = this.createPortal(
-        dom,
-        <OpenReactComponent loc={this.props.to} view={view} />
-      );
+      if(ValdidateComponentArgs(this.component)){
+        dom.style.display = "inline-block"; // want it inline for the open-close widget
+        this.portalCleanup = this.createPortal(
+          dom,
+          <OpenReactComponent loc={this.props.to} view={view} />
+        );
+      }
     } else
       this.portalCleanup = this.createPortal(
         dom,

From 20f223f34246ceef9997b05ba7508594f81cbbbd Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 15:44:17 +0200
Subject: [PATCH 075/106] yeyohellohi

---
 src/editor/codemirror/helper-widgets/openWidgets.tsx   |  2 ++
 .../codemirror/helper-widgets/reactWidgetExtension.tsx |  3 ++-
 .../codemirror/helper-widgets/widgetArgParser.tsx      | 10 ++++++++++
 3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/openWidgets.tsx b/src/editor/codemirror/helper-widgets/openWidgets.tsx
index f8df3e5b4..22a236005 100644
--- a/src/editor/codemirror/helper-widgets/openWidgets.tsx
+++ b/src/editor/codemirror/helper-widgets/openWidgets.tsx
@@ -22,3 +22,5 @@ export const OpenReactComponent = ({
     </HStack>
   );
 };
+
+
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 607784c42..5554f7858 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -10,6 +10,7 @@ import { PortalFactory } from "../CodeMirror";
 import React from "react";
 import { createWidget } from "./widgetArgParser";
 import { OpenReactComponent, openWidgetEffect } from "./openWidgets";
+import { ValidateComponentArgs } from "./widgetArgParser";
 
 export interface WidgetProps {
   // Note: always an array, can be singleton
@@ -43,7 +44,7 @@ class Widget extends WidgetType {
     const dom = document.createElement("div");
 
     if (this.inline) {
-      if(ValdidateComponentArgs(this.component)){
+      if (ValidateComponentArgs(this.component)) {
         dom.style.display = "inline-block"; // want it inline for the open-close widget
         this.portalCleanup = this.createPortal(
           dom,
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 5d584f979..850f2a601 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -90,3 +90,13 @@ function getTypes(nodes: SyntaxNode[]): string[] {
   });
   return types;
 }
+
+export function ValidateComponentArgs(name: React.ComponentType<any>): Boolean {
+  switch (name) {
+    case MicrobitMultiplePixelComponent:
+      return true;
+    case MicrobitSinglePixelComponent:
+      return false;
+  }
+  return true;
+}

From 07eb135238ccd6dc73b94ad97e8849ee466b5c19 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 09:57:47 -0400
Subject: [PATCH 076/106] added ranges into setPixel

---
 .../helper-widgets/setPixelWidget.tsx         | 22 ++++++++++++++-----
 .../helper-widgets/widgetArgParser.tsx        |  2 +-
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index aa8b2749f..364f5261a 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -131,7 +131,7 @@ export const MicrobitSinglePixelComponent = ({
   view: EditorView;
 }) => {
   let args = props.args;
-  //let ranges = props.ranges;
+  let ranges = props.ranges;
   let types = props.types;
   let from = props.from;
   let to = props.to;
@@ -151,11 +151,23 @@ export const MicrobitSinglePixelComponent = ({
   const handleSelectPixel = (pixel: Pixel) => {
     const { x, y, brightness } = pixel;
     view.dispatch({
-      changes: {
-        from: from,
-        to: to,
-        insert: `(${x}, ${y}, ${brightness}) `,
+      changes: [{
+        from: ranges[0].from,
+        to: ranges[0].to,
+        insert: `${x}`,
       },
+      {
+        from: ranges[1].from,
+        to: ranges[1].to,
+        insert: `${y}`,
+      },
+      {
+        from: ranges[2].from,
+        to: ranges[2].to,
+        insert: `${brightness}`,
+      },
+      ],
+      effects: [openWidgetEffect.of(to)],
     });
   };
 
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 46c733aea..597034466 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -96,7 +96,7 @@ export function ValidateComponentArgs(name: React.ComponentType<any>): Boolean {
     case MicrobitMultiplePixelComponent:
       return true;
     case MicrobitSinglePixelComponent:
-      return false;
+      return true;
   }
   return true;
 }

From dc7a3807a6557559361f00df02a770542043b53d Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 15:58:24 +0200
Subject: [PATCH 077/106] .

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 46c733aea..597034466 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -96,7 +96,7 @@ export function ValidateComponentArgs(name: React.ComponentType<any>): Boolean {
     case MicrobitMultiplePixelComponent:
       return true;
     case MicrobitSinglePixelComponent:
-      return false;
+      return true;
   }
   return true;
 }

From 2faaf9c4fc89bc27a4bdd28baae8118f8745f96c Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 15:58:43 +0200
Subject: [PATCH 078/106] .

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 597034466..46c733aea 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -96,7 +96,7 @@ export function ValidateComponentArgs(name: React.ComponentType<any>): Boolean {
     case MicrobitMultiplePixelComponent:
       return true;
     case MicrobitSinglePixelComponent:
-      return true;
+      return false;
   }
   return true;
 }

From d2658aed9d37a2dff452f3a0dacbfedc7c4c8be9 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 10:03:41 -0400
Subject: [PATCH 079/106] Comments dont disrupt arg parser

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 597034466..b854f32b7 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -55,7 +55,8 @@ function getChildNodes(node: SyntaxNode): SyntaxNode[] {
   let child = node.firstChild?.nextSibling;
   let children = [];
   while (child && child.name !== ")") {
-    if (child.name !== ",") children.push(child);
+    console.log(child.name);
+    if (child.name !== "," && child.name !== "Comment") children.push(child);
     child = child.nextSibling;
   }
   return children;

From c0c4c391dc92bf673e05f59f5975136c2d3ab869 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 10:04:19 -0400
Subject: [PATCH 080/106] b

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index b854f32b7..b163b536d 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -92,7 +92,7 @@ function getTypes(nodes: SyntaxNode[]): string[] {
   return types;
 }
 
-export function ValidateComponentArgs(name: React.ComponentType<any>): Boolean {
+export function ValidateComponentArgs(name: React.ComponentType<any>): boolean {
   switch (name) {
     case MicrobitMultiplePixelComponent:
       return true;

From f715d7b586ddcfe6eaedd005c5997d6f3361b519 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 9 Apr 2024 10:09:08 -0400
Subject: [PATCH 081/106] audio.SoundEffect

---
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index b163b536d..f143ee157 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -28,11 +28,12 @@ export function createWidget(
     case "Image":
       component = MicrobitMultiplePixelComponent;
       break;
-    case "SoundEffect":
+    case "audio.SoundEffect":
       component = SoundComponent;
       break;
     default:
       // No widget implemented for this function
+      console.log("No widget implemented for this function: " + name);
       return null;
   }
   if (component) {
@@ -98,6 +99,9 @@ export function ValidateComponentArgs(name: React.ComponentType<any>): boolean {
       return true;
     case MicrobitSinglePixelComponent:
       return true;
+    case SoundComponent:
+      return true;
+    default:
+      return false;
   }
-  return true;
 }

From a0d10eea7de6c90453071099b40966968a53f933 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Tue, 9 Apr 2024 16:43:31 +0200
Subject: [PATCH 082/106] .

---
 .../helper-widgets/setPixelWidget.tsx         | 35 ++++++++++---------
 .../helper-widgets/showImageWidget.tsx        |  4 +--
 2 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 364f5261a..f9c1855dd 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -39,9 +39,9 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   };
 
   return (
-    <Box display="flex" flexDirection="row" justifyContent="flex-start">
+    <Box display="flex" flexDirection="row" justifyContent="flex-start" bg = "lightgray">
       <Box ml="10px" style={{ marginRight: "4px" }}>
-        <Button size="xs" onClick={onCloseClick}>
+        <Button size="xs" onClick={onCloseClick} bg = "white">
           X
         </Button>
       </Box>
@@ -151,21 +151,22 @@ export const MicrobitSinglePixelComponent = ({
   const handleSelectPixel = (pixel: Pixel) => {
     const { x, y, brightness } = pixel;
     view.dispatch({
-      changes: [{
-        from: ranges[0].from,
-        to: ranges[0].to,
-        insert: `${x}`,
-      },
-      {
-        from: ranges[1].from,
-        to: ranges[1].to,
-        insert: `${y}`,
-      },
-      {
-        from: ranges[2].from,
-        to: ranges[2].to,
-        insert: `${brightness}`,
-      },
+      changes: [
+        {
+          from: ranges[0].from,
+          to: ranges[0].to,
+          insert: `${x}`,
+        },
+        {
+          from: ranges[1].from,
+          to: ranges[1].to,
+          insert: `${y}`,
+        },
+        {
+          from: ranges[2].from,
+          to: ranges[2].to,
+          insert: `${brightness}`,
+        },
       ],
       effects: [openWidgetEffect.of(to)],
     });
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index e4c815adb..3b3d5d805 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -41,9 +41,9 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   };
 
   return (
-    <Box display="flex" flexDirection="row" justifyContent="flex-start">
+    <Box display="flex" flexDirection="row" justifyContent="flex-start" bg = "lightgray">
       <Box ml="10px" style={{ marginRight: "4px" }}>
-        <Button size="xs" onClick={onCloseClick}>
+        <Button size="xs" onClick={onCloseClick} bg = "white">
           X
         </Button>
       </Box>

From d612f844724a3316d5ba4ff44bc4c96b0cd16a70 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Wed, 10 Apr 2024 20:19:44 +0200
Subject: [PATCH 083/106] validation of args

---
 .../helper-widgets/reactWidgetExtension.tsx   |  2 +-
 .../helper-widgets/setPixelWidget.tsx         | 52 +++++++++----------
 .../helper-widgets/showImageWidget.tsx        |  9 +++-
 .../helper-widgets/widgetArgParser.tsx        | 14 ++++-
 4 files changed, 45 insertions(+), 32 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 5554f7858..e3201bb99 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -44,7 +44,7 @@ class Widget extends WidgetType {
     const dom = document.createElement("div");
 
     if (this.inline) {
-      if (ValidateComponentArgs(this.component)) {
+      if (ValidateComponentArgs(this.component, [], [])){
         dom.style.display = "inline-block"; // want it inline for the open-close widget
         this.portalCleanup = this.createPortal(
           dom,
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index f9c1855dd..42cf03c8d 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -39,9 +39,14 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   };
 
   return (
-    <Box display="flex" flexDirection="row" justifyContent="flex-start" bg = "lightgray">
+    <Box
+      display="flex"
+      flexDirection="row"
+      justifyContent="flex-start"
+      bg="lightgray"
+    >
       <Box ml="10px" style={{ marginRight: "4px" }}>
-        <Button size="xs" onClick={onCloseClick} bg = "white">
+        <Button size="xs" onClick={onCloseClick} bg="white">
           X
         </Button>
       </Box>
@@ -102,27 +107,6 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   );
 };
 
-const parseArgs = (args: string[], types: string[]): Pixel | null => {
-  if (args.length > 3) {
-    return null;
-  }
-  const parsedArgs: number[] = [];
-  for (let i = 0; i < args.length; i++) {
-    let arg = args[i];
-    if (types[i] === "Number") {
-      parsedArgs.push(parseInt(arg));
-    } else if (arg === ",") {
-      parsedArgs.push(0);
-    } else {
-      return null;
-    }
-  }
-  while (parsedArgs.length < 3) {
-    parsedArgs.push(0);
-  }
-  return { x: parsedArgs[0], y: parsedArgs[1], brightness: parsedArgs[2] };
-};
-
 export const MicrobitSinglePixelComponent = ({
   props,
   view,
@@ -135,12 +119,8 @@ export const MicrobitSinglePixelComponent = ({
   let types = props.types;
   let from = props.from;
   let to = props.to;
-  console.log(args);
-  console.log(types);
-  const selectedPixel = parseArgs(args, types);
 
-  if (selectedPixel == null) {
-  }
+  const selectedPixel = parseArgs(args, types);
 
   const handleCloseClick = () => {
     view.dispatch({
@@ -180,3 +160,19 @@ export const MicrobitSinglePixelComponent = ({
     />
   );
 };
+
+const parseArgs = (args: string[], types: string[]): Pixel => {
+  const parsedArgs: number[] = [];
+  for (let i = 0; i < args.length; i++) {
+    let arg = args[i];
+    if (types[i] === "Number") {
+      parsedArgs.push(parseInt(arg));
+    } else {
+      parsedArgs.push(0);
+    }
+  }
+  while (parsedArgs.length < 3) {
+    parsedArgs.push(0);
+  }
+  return { x: parsedArgs[0], y: parsedArgs[1], brightness: parsedArgs[2] };
+};
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 3b3d5d805..251313234 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -41,9 +41,14 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
   };
 
   return (
-    <Box display="flex" flexDirection="row" justifyContent="flex-start" bg = "lightgray">
+    <Box
+      display="flex"
+      flexDirection="row"
+      justifyContent="flex-start"
+      bg="lightgray"
+    >
       <Box ml="10px" style={{ marginRight: "4px" }}>
-        <Button size="xs" onClick={onCloseClick} bg = "white">
+        <Button size="xs" onClick={onCloseClick} bg="white">
           X
         </Button>
       </Box>
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index f143ee157..9edcb4446 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -93,11 +93,23 @@ function getTypes(nodes: SyntaxNode[]): string[] {
   return types;
 }
 
-export function ValidateComponentArgs(name: React.ComponentType<any>): boolean {
+export function ValidateComponentArgs(
+  name: React.ComponentType<any>,
+  args: string[],
+  types: string[]
+): boolean {
   switch (name) {
     case MicrobitMultiplePixelComponent:
       return true;
     case MicrobitSinglePixelComponent:
+      if (args.length > 3) {
+        return false;
+      }
+      for (let i = 0; i < args.length; i++) {
+        if (types[i] !== "Number" && args[i] != ",") {
+          return false;
+        }
+      }
       return true;
     case SoundComponent:
       return true;

From 8150a5586882d8fd25f15cef08991920900240b5 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Wed, 10 Apr 2024 20:25:16 +0200
Subject: [PATCH 084/106] ranges n stuff

---
 src/editor/codemirror/helper-widgets/showImageWidget.tsx | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 251313234..37e9feb11 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -113,7 +113,7 @@ export const MicrobitMultiplePixelComponent = ({
   view: EditorView;
 }) => {
   let args = props.args;
-  //let ranges = props.ranges;
+  let ranges = props.ranges;
   let types = props.types;
   let from = props.from;
   let to = props.to;
@@ -137,8 +137,8 @@ export const MicrobitMultiplePixelComponent = ({
     console.log(insertion);
     view.dispatch({
       changes: {
-        from: from,
-        to: to,
+        from: ranges[0].from,
+        to: ranges[0].to,
         insert: insertion,
       },
     });
@@ -190,7 +190,7 @@ const parseArgs = (args: string[]): number[][] => {
 };
 
 function pixelsToString(pixels: number[][]): string {
-  let outputString = "(";
+  let outputString = "";
   for (let y = 0; y < 5; y++) {
     for (let x = 0; x < 5; x++) {
       outputString += pixels[y][x].toString();
@@ -198,6 +198,5 @@ function pixelsToString(pixels: number[][]): string {
     outputString += ":";
   }
   outputString = outputString.slice(0, -1);
-  outputString += ")";
   return outputString;
 }

From 1482a0f4831e202a376062d4f1557cfb4270f4c2 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Wed, 10 Apr 2024 21:36:32 +0200
Subject: [PATCH 085/106] .

---
 src/editor/codemirror/helper-widgets/openWidgets.tsx        | 2 --
 .../codemirror/helper-widgets/reactWidgetExtension.tsx      | 2 +-
 src/editor/codemirror/helper-widgets/setPixelWidget.tsx     | 3 +++
 src/editor/codemirror/helper-widgets/showImageWidget.tsx    | 6 +-----
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx    | 2 ++
 5 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/openWidgets.tsx b/src/editor/codemirror/helper-widgets/openWidgets.tsx
index 22a236005..f8df3e5b4 100644
--- a/src/editor/codemirror/helper-widgets/openWidgets.tsx
+++ b/src/editor/codemirror/helper-widgets/openWidgets.tsx
@@ -22,5 +22,3 @@ export const OpenReactComponent = ({
     </HStack>
   );
 };
-
-
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index e3201bb99..60f017681 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -44,7 +44,7 @@ class Widget extends WidgetType {
     const dom = document.createElement("div");
 
     if (this.inline) {
-      if (ValidateComponentArgs(this.component, [], [])){
+      if (ValidateComponentArgs(this.component, [], [])) {
         dom.style.display = "inline-block"; // want it inline for the open-close widget
         this.portalCleanup = this.createPortal(
           dom,
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 42cf03c8d..53c490f64 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -29,10 +29,12 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   onCloseClick,
 }) => {
   const { x, y, brightness } = initialPixel ?? { x: 0, y: 0, brightness: 9 };
+
   const handlePixelClick = (x: number, y: number) => {
     const newPixel: Pixel = { x, y, brightness };
     onPixelClick(newPixel);
   };
+
   const handleSliderChange = (value: number) => {
     const updatedPixel: Pixel = { x, y, brightness: value };
     onPixelClick(updatedPixel);
@@ -171,6 +173,7 @@ const parseArgs = (args: string[], types: string[]): Pixel => {
       parsedArgs.push(0);
     }
   }
+  //replace missing arguments with 0
   while (parsedArgs.length < 3) {
     parsedArgs.push(0);
   }
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 37e9feb11..89f0eb8e2 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -161,21 +161,18 @@ export const MicrobitMultiplePixelComponent = ({
 
 const parseArgs = (args: string[]): number[][] => {
   const defaultPixels = Array.from({ length: 5 }, () => Array(5).fill(0));
-  // If args is empty, return a 5x5 array filled with zeros
+  //if args is empty, return a 5x5 array filled with zeros
   if (args.length === 0) {
     return defaultPixels;
   }
-
   if (args.length !== 1) {
     return defaultPixels;
   }
   const argString = args[0];
   const rows = argString.split(":");
-
   if (rows.length !== 5) {
     return defaultPixels;
   }
-
   const numbers: number[][] = [];
   for (let row of rows) {
     row = row.trim();
@@ -185,7 +182,6 @@ const parseArgs = (args: string[]): number[][] => {
     const rowNumbers = row.split("").map(Number);
     numbers.push(rowNumbers);
   }
-
   return numbers;
 };
 
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 9edcb4446..6bcac7f1e 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -102,9 +102,11 @@ export function ValidateComponentArgs(
     case MicrobitMultiplePixelComponent:
       return true;
     case MicrobitSinglePixelComponent:
+      //if more than 3 arguments, don't open
       if (args.length > 3) {
         return false;
       }
+      //if some arguments are not numbers or empty, don't open
       for (let i = 0; i < args.length; i++) {
         if (types[i] !== "Number" && args[i] != ",") {
           return false;

From 63c206da052ed23703ef33bb2f016b2a8cb23e35 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Thu, 11 Apr 2024 23:43:40 -0400
Subject: [PATCH 086/106] ValidateComponentArgs args types

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 60f017681..af419953a 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -44,7 +44,7 @@ class Widget extends WidgetType {
     const dom = document.createElement("div");
 
     if (this.inline) {
-      if (ValidateComponentArgs(this.component, [], [])) {
+      if (ValidateComponentArgs(this.component, this.props.args, this.props.types)) {
         dom.style.display = "inline-block"; // want it inline for the open-close widget
         this.portalCleanup = this.createPortal(
           dom,

From f83adbe0d1595290a5c19a27fcbb1ca96345f4f9 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Thu, 11 Apr 2024 23:56:55 -0400
Subject: [PATCH 087/106] set_pixel autofills when args are not fully written

---
 .../helper-widgets/reactWidgetExtension.tsx   |  5 ++
 .../helper-widgets/setPixelWidget.tsx         | 56 ++++++++++++-------
 2 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index af419953a..a7780932f 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -43,6 +43,11 @@ class Widget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
+    console.log("TEST")
+    console.log(this.props.args.length);
+    console.log(this.props.types.length)
+    console.log(this.props.ranges.length)
+
     if (this.inline) {
       if (ValidateComponentArgs(this.component, this.props.args, this.props.types)) {
         dom.style.display = "inline-block"; // want it inline for the open-close widget
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 53c490f64..29473a515 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -132,26 +132,42 @@ export const MicrobitSinglePixelComponent = ({
 
   const handleSelectPixel = (pixel: Pixel) => {
     const { x, y, brightness } = pixel;
-    view.dispatch({
-      changes: [
-        {
-          from: ranges[0].from,
-          to: ranges[0].to,
-          insert: `${x}`,
-        },
-        {
-          from: ranges[1].from,
-          to: ranges[1].to,
-          insert: `${y}`,
-        },
-        {
-          from: ranges[2].from,
-          to: ranges[2].to,
-          insert: `${brightness}`,
-        },
-      ],
-      effects: [openWidgetEffect.of(to)],
-    });
+    if(ranges.length === 3){
+      view.dispatch({
+        changes: [
+          {
+            from: ranges[0].from,
+            to: ranges[0].to,
+            insert: `${x}`,
+          },
+          {
+            from: ranges[1].from,
+            to: ranges[1].to,
+            insert: `${y}`,
+          },
+          {
+            from: ranges[2].from,
+            to: ranges[2].to,
+            insert: `${brightness}`,
+          },
+        ],
+        effects: [openWidgetEffect.of(to)],
+      });
+    }
+    else{
+      let vals = `${x},${y},${brightness}`;
+      view.dispatch({
+        changes: [
+          {
+            from: from+1,
+            to: to-1,
+            insert: vals,
+          },
+        ],
+        effects: [openWidgetEffect.of(vals.length + from + 2)],
+      });
+      console.log(from, to, )
+    }
   };
 
   return (

From 6b783ad75807d0e51fd7eab2dca3661b2dc255e2 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Thu, 11 Apr 2024 23:57:30 -0400
Subject: [PATCH 088/106] removed test comments

---
 .../codemirror/helper-widgets/reactWidgetExtension.tsx       | 5 -----
 src/editor/codemirror/helper-widgets/setPixelWidget.tsx      | 1 -
 2 files changed, 6 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index a7780932f..af419953a 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -43,11 +43,6 @@ class Widget extends WidgetType {
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    console.log("TEST")
-    console.log(this.props.args.length);
-    console.log(this.props.types.length)
-    console.log(this.props.ranges.length)
-
     if (this.inline) {
       if (ValidateComponentArgs(this.component, this.props.args, this.props.types)) {
         dom.style.display = "inline-block"; // want it inline for the open-close widget
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 29473a515..be2598cf1 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -166,7 +166,6 @@ export const MicrobitSinglePixelComponent = ({
         ],
         effects: [openWidgetEffect.of(vals.length + from + 2)],
       });
-      console.log(from, to, )
     }
   };
 

From 4f5e267c26edcd92b1b5194af20549a5c1a44c93 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Fri, 12 Apr 2024 09:47:05 +0200
Subject: [PATCH 089/106] .

---
 .../helper-widgets/setPixelWidget.tsx         |  2 +-
 .../helper-widgets/showImageWidget.tsx        | 30 ++++++++++++++-----
 .../helper-widgets/widgetArgParser.tsx        |  4 +--
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index be2598cf1..b61725d6b 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -188,7 +188,7 @@ const parseArgs = (args: string[], types: string[]): Pixel => {
       parsedArgs.push(0);
     }
   }
-  //replace missing arguments with 0
+  // Replace missing arguments with 0
   while (parsedArgs.length < 3) {
     parsedArgs.push(0);
   }
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 89f0eb8e2..3f907b011 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -135,13 +135,27 @@ export const MicrobitMultiplePixelComponent = ({
   const updateView = () => {
     let insertion = pixelsToString(selectedPixels);
     console.log(insertion);
-    view.dispatch({
-      changes: {
-        from: ranges[0].from,
-        to: ranges[0].to,
-        insert: insertion,
-      },
-    });
+    if (ranges.length === 1) {
+      view.dispatch({
+        changes: {
+          from: ranges[0].from,
+          to: ranges[0].to,
+          insert: insertion,
+        },
+        effects: [openWidgetEffect.of(to)],
+      });
+    } else {
+      view.dispatch({
+        changes: [
+          {
+            from: from + 1,
+            to: to - 1,
+            insert: insertion,
+          },
+        ],
+        effects: [openWidgetEffect.of(insertion.length + from + 2)],
+      });
+    }
   };
 
   const handleCloseClick = () => {
@@ -161,7 +175,7 @@ export const MicrobitMultiplePixelComponent = ({
 
 const parseArgs = (args: string[]): number[][] => {
   const defaultPixels = Array.from({ length: 5 }, () => Array(5).fill(0));
-  //if args is empty, return a 5x5 array filled with zeros
+  // If args is empty, return a 5x5 array filled with zeros
   if (args.length === 0) {
     return defaultPixels;
   }
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 6bcac7f1e..d956089ed 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -102,11 +102,11 @@ export function ValidateComponentArgs(
     case MicrobitMultiplePixelComponent:
       return true;
     case MicrobitSinglePixelComponent:
-      //if more than 3 arguments, don't open
+      // If more than 3 arguments, don't open
       if (args.length > 3) {
         return false;
       }
-      //if some arguments are not numbers or empty, don't open
+      // If some arguments are not numbers or empty, don't open
       for (let i = 0; i < args.length; i++) {
         if (types[i] !== "Number" && args[i] != ",") {
           return false;

From afdf2be3e0b237993a27520053dcc17b37f6e408 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Fri, 12 Apr 2024 11:06:03 +0200
Subject: [PATCH 090/106] .

---
 .../helper-widgets/setPixelWidget.tsx         | 22 ++++++++++++++-----
 .../helper-widgets/showImageWidget.tsx        | 13 ++++++++++-
 2 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index b61725d6b..1ca2b4bf2 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -40,6 +40,11 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
     onPixelClick(updatedPixel);
   };
 
+  const calculateColor = () => {
+    const red = brightness * 25.5;
+    return `rgb(${red}, 0, 0)`;
+  };
+
   return (
     <Box
       display="flex"
@@ -68,11 +73,17 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
                     h="15px"
                     w="15px"
                     p={0}
+                    borderRadius={0}
                     bgColor={
                       gridX === x && gridY === y
                         ? `rgba(255, 0, 0, ${brightness / 9})`
                         : "rgba(255, 255, 255, 0)"
                     }
+                    border={
+                      gridX === x && gridY === y
+                        ? "2px solid white"
+                        : "0.5px solid white"
+                    }
                     _hover={{
                       bgColor:
                         gridX === x && gridY === y
@@ -100,7 +111,7 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
           onChange={handleSliderChange}
         >
           <SliderTrack>
-            <SliderFilledTrack />
+            <SliderFilledTrack bg={calculateColor()} />
           </SliderTrack>
           <SliderThumb />
         </Slider>
@@ -132,7 +143,7 @@ export const MicrobitSinglePixelComponent = ({
 
   const handleSelectPixel = (pixel: Pixel) => {
     const { x, y, brightness } = pixel;
-    if(ranges.length === 3){
+    if (ranges.length === 3) {
       view.dispatch({
         changes: [
           {
@@ -153,14 +164,13 @@ export const MicrobitSinglePixelComponent = ({
         ],
         effects: [openWidgetEffect.of(to)],
       });
-    }
-    else{
+    } else {
       let vals = `${x},${y},${brightness}`;
       view.dispatch({
         changes: [
           {
-            from: from+1,
-            to: to-1,
+            from: from + 1,
+            to: to - 1,
             insert: vals,
           },
         ],
diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 3f907b011..071e4d6d3 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -40,6 +40,11 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
     }
   };
 
+  const calculateColor = () => {
+    const red = currentBrightness * 25.5;
+    return `rgb(${red}, 0, 0)`;
+  };
+
   return (
     <Box
       display="flex"
@@ -68,6 +73,12 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
                     h="15px"
                     w="15px"
                     p={0}
+                    borderRadius={0}
+                    border={
+                      selectedPixel?.x === x && selectedPixel.y === y
+                        ? "2px solid white"
+                        : "0.5px solid white"
+                    }
                     bgColor={`rgba(255, 0, 0, ${brightness / 9})`}
                     _hover={{
                       bgColor:
@@ -96,7 +107,7 @@ const MicrobitMultiplePixelsGrid: React.FC<MultiMicrobitGridProps> = ({
           onChange={(value) => handleBrightnessChange(value)}
         >
           <SliderTrack>
-            <SliderFilledTrack />
+            <SliderFilledTrack bg={calculateColor()} />
           </SliderTrack>
           <SliderThumb />
         </Slider>

From b111f666507312de6fc30ec6bff454cd9f2ef5d6 Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Tue, 16 Apr 2024 12:08:15 +0100
Subject: [PATCH 091/106] basic dynamic waveform!

---
 .../codemirror/helper-widgets/soundWidget.tsx | 22 ++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index 5dd98d5cf..cade7dd0f 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -125,19 +125,36 @@ const TripleSliderWidget: React.FC<{
   slider3Props: SliderProps;
   isOpen: boolean;
 }> = ({ slider1Props, slider2Props, slider3Props, isOpen }) => {
+
+  const [waveHeight, setWaveHeight] = useState(50);
+  const [waveLength, setWaveLength] = useState(50);
+
+  const handleSlider1Change = (value: number) => {
+    slider1Props.onChange(value);
+    setWaveHeight(value);
+  };
+
+  const handleSlider2Change = (value: number) => {
+    slider2Props.onChange(value);
+    setWaveLength(value); // 
+  };
+
   if (!isOpen) return null;
   return (
     <div>
       <div style={{ display: "flex", justifyContent: "left" }}>
         <div style={{ marginRight: "40px" }}>
-          <Slider {...slider1Props} />
+          <Slider {...slider1Props} onChange={handleSlider1Change} />
         </div>
         <div style={{ marginRight: "40px" }}>
-          <Slider {...slider2Props} />
+          <Slider {...slider2Props} onChange={handleSlider2Change} />
         </div>
         <div>
           <Slider {...slider3Props} />
         </div>
+        <svg width={waveLength} height={waveHeight} style={{ flexGrow: 1 }}>
+           <path d={`M0,${waveHeight / 2} Q${waveLength / 4},0 ${waveLength / 2},${waveHeight / 2} T${waveLength},${waveHeight / 2}`} stroke="black" fill="none" />
+        </svg>
       </div>
     </div>
   );
@@ -163,7 +180,6 @@ export const SoundComponent = ({
   };
   const [isSoundEditorOpen, setIsSoundEditorOpen] = useState(false);
   const buttonLabel = isSoundEditorOpen ? "Close" : "Open";
-
   const handleButtonClick = () => {
     setIsSoundEditorOpen(!isSoundEditorOpen);
     // Toggle the state to open/close the DualSlider

From b69bb80f7dbf8b38078daf041e355c3deba8a0f4 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 16 Apr 2024 13:51:02 +0100
Subject: [PATCH 092/106] comments

---
 src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx | 3 +--
 src/editor/codemirror/helper-widgets/setPixelWidget.tsx       | 2 +-
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx      | 1 +
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index af419953a..97a6dfea9 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -56,7 +56,6 @@ class Widget extends WidgetType {
         dom,
         <this.component props={this.props} view={view} />
       );
-    //else this.portalCleanup = this.createPortal(dom, React.createElement(this.component, this.props, view));
     return dom;
   }
 
@@ -71,7 +70,7 @@ class Widget extends WidgetType {
   }
 }
 
-// Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places toy widget there
+// Iterates through the syntax tree, finding occurences of SoundEffect ArgList, and places widget there
 export const reactWidgetExtension = (
   createPortal: PortalFactory
 ): Extension => {
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 1ca2b4bf2..01eaabb90 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -46,7 +46,7 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   };
 
   return (
-    <Box
+    <Box // TODO: copy to allow other widgets to access bg and close
       display="flex"
       flexDirection="row"
       justifyContent="flex-start"
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index d956089ed..371b0f2b4 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -29,6 +29,7 @@ export function createWidget(
       component = MicrobitMultiplePixelComponent;
       break;
     case "audio.SoundEffect":
+    case "SoundEffect":
       component = SoundComponent;
       break;
     default:

From 2efe920f14dbe7d676f3bf4d99cfb64d6a319924 Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Fri, 19 Apr 2024 21:08:08 +0100
Subject: [PATCH 093/106] Waveform know changes according to most parameters!!!

---
 .../codemirror/helper-widgets/soundWidget.tsx | 171 ++++++++++++++----
 1 file changed, 134 insertions(+), 37 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index cade7dd0f..d617a2de6 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -19,9 +19,11 @@ interface SliderProps {
   onChange: (value: number) => void;
   sliderStyle?: React.CSSProperties;
   label: string;
+  vertical: boolean;
+  colour: string;
 }
 
-const DurationSliderProps: SliderProps = {
+const startVolProps: SliderProps = {
   min: 0,
   max: 100,
   step: 1,
@@ -37,10 +39,12 @@ const DurationSliderProps: SliderProps = {
     border: "none", // Remove the border of the slider track
     outline: "none", // Remove the outline when focused
   },
-  label: "Duration",
+  label: "Start Vol",
+  vertical: true,
+  colour: 'red'
 };
 
-const endSliderProps: SliderProps = {
+const endFrequencySliderProps: SliderProps = {
   min: 0,
   max: 100,
   step: 1,
@@ -57,9 +61,11 @@ const endSliderProps: SliderProps = {
     outline: "none", // Remove the outline when focused
   },
   label: "End Freq",
+  vertical: true,
+  colour: 'green'
 };
 
-const startSliderProps: SliderProps = {
+const startFrequencySliderProps: SliderProps = {
   min: 0,
   max: 100,
   step: 1,
@@ -76,14 +82,38 @@ const startSliderProps: SliderProps = {
     outline: "none", // Remove the outline when focused
   },
   label: "Start Freq",
+  vertical: true,
+  colour: 'blue'
 };
 
+const endVolProps: SliderProps = {
+  min: 0,
+  max: 100,
+  step: 1,
+  defaultValue: 50,
+  onChange: (value) => {
+    console.log("Slider value changed:", value);
+  },
+  sliderStyle: {
+    width: "200%", // Adjust the width of the slider
+    height: "100px", // Adjust the height of the slider
+    backgroundColor: "lightgray", // Change the background color of the slider
+    borderRadius: "10px", // Apply rounded corners to the slider track
+    border: "none", // Remove the border of the slider track
+    outline: "none", // Remove the outline when focused
+  },
+  label: "End Vol",
+  vertical: true,
+  colour: 'yellow'
+};
+
+
 const customSliderStyle: React.CSSProperties = {
   width: "80%", // Adjust the width of the slider
   height: "20px", // Adjust the height of the slider
 };
 
-const Slider: React.FC<SliderProps> = ({
+const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = ({
   min,
   max,
   step,
@@ -91,6 +121,8 @@ const Slider: React.FC<SliderProps> = ({
   onChange,
   sliderStyle,
   label,
+  vertical,
+  colour
 }) => {
   const [value, setValue] = useState(defaultValue);
 
@@ -104,14 +136,24 @@ const Slider: React.FC<SliderProps> = ({
     <div
       style={{ display: "flex", flexDirection: "column", alignItems: "left" }}
     >
-      <input
-        type="range"
-        min={min}
-        max={max}
-        step={step}
-        value={value}
-        onChange={handleChange}
-      />
+    <input
+      type="range"
+      min={min}
+      max={max}
+      step={step}
+      defaultValue={defaultValue}
+      onChange={handleChange}
+      style={{
+        //writingMode: 'bt-lr', // Adjust writing mode for vertical slider
+        width: vertical ? '100px' : '150px', // Use width to control vertical slider size
+        height: vertical ? '150px' : '40px', // Use height to control vertical slider size
+        transform: vertical ? 'rotate(270deg)' : 'none', // Rotate slider to vertical orientation
+        background: vertical ? 'none' : '#d3d3d3', // Background of the track (horizontal)
+        accentColor: `${colour}`, // Thumb color in modern browsers
+        //WebkitAppearance: 'slider-vertical',
+       //MozAppearance: vertical ? 'slider-vertical' : undefined,
+      }}
+    />
       <label style={{ fontSize: "16px" }}>
         {label}: {value}
       </label>
@@ -123,38 +165,88 @@ const TripleSliderWidget: React.FC<{
   slider1Props: SliderProps;
   slider2Props: SliderProps;
   slider3Props: SliderProps;
+  slider4Props: SliderProps;
   isOpen: boolean;
-}> = ({ slider1Props, slider2Props, slider3Props, isOpen }) => {
+}> = ({ slider1Props, slider2Props, slider3Props, slider4Props, isOpen }) => {
 
-  const [waveHeight, setWaveHeight] = useState(50);
-  const [waveLength, setWaveLength] = useState(50);
+  const [startAmplitude, setStartAmplitude] = useState(slider3Props.defaultValue);
+  const [endAmplitude, setEndAmplitude] = useState(50);
+  const [initialFrequency, setInitialFrequency] = useState(slider1Props.defaultValue);
+  const [endFrequency, setEndFrequency] = useState(slider2Props.defaultValue);
 
   const handleSlider1Change = (value: number) => {
     slider1Props.onChange(value);
-    setWaveHeight(value);
+    setInitialFrequency(value);
   };
 
   const handleSlider2Change = (value: number) => {
     slider2Props.onChange(value);
-    setWaveLength(value); // 
+    setEndFrequency(value); // 
+  };
+
+  const handleSlider3Change = (value: number) => {
+    slider1Props.onChange(value);
+    setStartAmplitude(value);
+  };
+
+  const handleSlider4Change = (value: number) => {
+    slider1Props.onChange(value);
+    setEndAmplitude(value);
   };
 
-  if (!isOpen) return null;
+
+
+  const generateWavePath = () => {
+    const waveLength = 400; // Width of the box
+    const pathData = [];
+    
+    // Calculate the change in frequency and amplitude over the length of the waveform
+    const frequencyDifference = endFrequency - initialFrequency;
+    const amplitudeDifference = endAmplitude - startAmplitude;
+
+    // Loop through the wave's width to generate the path
+    for (let x = 0; x <= waveLength; x++) {
+      // Calculate the frequency and amplitude at the current point
+      const currentFrequency = initialFrequency + (frequencyDifference * x) / waveLength;
+      const currentAmplitude = startAmplitude + (amplitudeDifference * x) / waveLength;
+
+      // Calculate the y-coordinate based on the current frequency and amplitude
+      const y = 50 + currentAmplitude * Math.sin(currentFrequency * x * (2 * Math.PI) / waveLength);
+      
+      // Add the point to the path data
+      pathData.push(`${x},${y}`);
+    }
+
+    // Join the path data points to create the path
+    return `M${pathData.join(' ')}`;
+  };
+
+  //if (!isOpen) return null;
   return (
     <div>
-      <div style={{ display: "flex", justifyContent: "left" }}>
-        <div style={{ marginRight: "40px" }}>
-          <Slider {...slider1Props} onChange={handleSlider1Change} />
-        </div>
-        <div style={{ marginRight: "40px" }}>
-          <Slider {...slider2Props} onChange={handleSlider2Change} />
-        </div>
-        <div>
-          <Slider {...slider3Props} />
-        </div>
-        <svg width={waveLength} height={waveHeight} style={{ flexGrow: 1 }}>
-           <path d={`M0,${waveHeight / 2} Q${waveLength / 4},0 ${waveLength / 2},${waveHeight / 2} T${waveLength},${waveHeight / 2}`} stroke="black" fill="none" />
-        </svg>
+      <div style={{ display: "flex", justifyContent: "flex-start", alignItems: "center" }}>
+          {/* Vertical Slider 1 */}
+          <div style={{ marginRight: "20px" }}>
+              <Slider {...slider1Props} onChange={handleSlider1Change} vertical />
+          </div>
+          {/* Vertical Slider 2 */}
+          <div style={{ marginRight: "20px" }}>
+              <Slider {...slider2Props} onChange={handleSlider2Change} vertical />
+          </div>
+          {/* Vertical Slider 3 */}
+          <div style={{ marginRight: "20px" }}>
+              <Slider {...slider3Props} onChange={handleSlider3Change} vertical />
+          </div>
+          {/* Vertical Slider 4 */}
+          <div style={{ marginRight: "20px" }}>
+              <Slider {...slider4Props} onChange={handleSlider4Change} vertical />
+          </div>
+          {/* Waveform box */}
+          <div style={{ width: '200px', height: '100px', border: '1px solid black' }}>
+              <svg width="100%" height="100%">
+                  <path d={generateWavePath()} stroke="black" fill="none" />
+              </svg>
+          </div>
       </div>
     </div>
   );
@@ -192,14 +284,19 @@ export const SoundComponent = ({
       insert:  //something from state of component`,
     },
     */
-
+// <Button onClick={handleButtonClick}>{buttonLabel} Sound Editor</Button>
   return (
     <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleButtonClick}>{buttonLabel} Sound Editor</Button>
+      <Box ml="10px" style={{ marginRight: "4px" }}>
+        <Button size="xs" onClick={handleCloseClick} bg="white">
+          X
+        </Button>
+      </Box>
       <TripleSliderWidget
-        slider1Props={startSliderProps}
-        slider2Props={endSliderProps}
-        slider3Props={DurationSliderProps}
+        slider1Props={startFrequencySliderProps}
+        slider2Props={endFrequencySliderProps}
+        slider3Props={startVolProps}
+        slider4Props={endVolProps}
         isOpen={isSoundEditorOpen}
       />
     </HStack>

From cdc0e815728bc7c6f68b3edbcde5c35b4bfb78b8 Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Sat, 20 Apr 2024 20:36:51 +0100
Subject: [PATCH 094/106] sound widget complete

---
 package-lock.json                             |   6 +
 package.json                                  |   1 +
 .../codemirror/helper-widgets/soundWidget.tsx | 185 +++++++++++++-----
 3 files changed, 146 insertions(+), 46 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 225f778d7..e629fd6cb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -47,6 +47,7 @@
         "lzma": "^2.3.2",
         "marked": "^4.0.15",
         "mobile-drag-drop": "^2.3.0-rc.2",
+        "perlin-noise": "^0.0.1",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "react-icons": "^4.8.0",
@@ -15851,6 +15852,11 @@
       "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
       "dev": true
     },
+    "node_modules/perlin-noise": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/perlin-noise/-/perlin-noise-0.0.1.tgz",
+      "integrity": "sha512-33wNN1FN7jZPF0ISkSF8BLag71wjBWzrpzd/m00iFsxtIhKeZ8VaKBQtzPX3TBegK9GYPXwGzR3oJp9v2T7QuQ=="
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
diff --git a/package.json b/package.json
index b7e3739bb..591c857da 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
     "lzma": "^2.3.2",
     "marked": "^4.0.15",
     "mobile-drag-drop": "^2.3.0-rc.2",
+    "perlin-noise": "^0.0.1",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "react-icons": "^4.8.0",
diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index d617a2de6..b34fbadaf 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -46,9 +46,9 @@ const startVolProps: SliderProps = {
 
 const endFrequencySliderProps: SliderProps = {
   min: 0,
-  max: 100,
+  max: 999,
   step: 1,
-  defaultValue: 50,
+  defaultValue: 500,
   onChange: (value) => {
     console.log("Slider value changed:", value);
   },
@@ -67,9 +67,9 @@ const endFrequencySliderProps: SliderProps = {
 
 const startFrequencySliderProps: SliderProps = {
   min: 0,
-  max: 100,
+  max: 999,
   step: 1,
-  defaultValue: 50,
+  defaultValue: 500,
   onChange: (value) => {
     console.log("Slider value changed:", value);
   },
@@ -104,13 +104,7 @@ const endVolProps: SliderProps = {
   },
   label: "End Vol",
   vertical: true,
-  colour: 'yellow'
-};
-
-
-const customSliderStyle: React.CSSProperties = {
-  width: "80%", // Adjust the width of the slider
-  height: "20px", // Adjust the height of the slider
+  colour: 'black'
 };
 
 const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = ({
@@ -133,34 +127,42 @@ const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = (
   };
 
   return (
-    <div
-      style={{ display: "flex", flexDirection: "column", alignItems: "left" }}
-    >
+    <div style={{ position: 'relative', height: '80px', width: '45px', display: "flex", flexDirection: "column", alignItems: "center" }}>
     <input
-      type="range"
-      min={min}
-      max={max}
-      step={step}
-      defaultValue={defaultValue}
-      onChange={handleChange}
-      style={{
-        //writingMode: 'bt-lr', // Adjust writing mode for vertical slider
-        width: vertical ? '100px' : '150px', // Use width to control vertical slider size
-        height: vertical ? '150px' : '40px', // Use height to control vertical slider size
-        transform: vertical ? 'rotate(270deg)' : 'none', // Rotate slider to vertical orientation
-        background: vertical ? 'none' : '#d3d3d3', // Background of the track (horizontal)
-        accentColor: `${colour}`, // Thumb color in modern browsers
-        //WebkitAppearance: 'slider-vertical',
-       //MozAppearance: vertical ? 'slider-vertical' : undefined,
-      }}
+        type="range"
+        min={min}
+        max={max}
+        step={step}
+        value={value}
+        onChange={handleChange}
+        style={{
+            position: 'absolute',
+            width: '115px', // Width of the slider
+            height: '40px', // Height of the slider
+            transform: 'rotate(-90deg)', // Rotate the slider to vertical orientation
+            accentColor: colour, 
+            bottom: '0%',
+        }}
     />
-      <label style={{ fontSize: "16px" }}>
-        {label}: {value}
-      </label>
+    <div
+        style={{
+            position: 'absolute',
+            left: 'calc(100% - 15px)', // Position the label to the right of the slider
+            bottom: `${((value - min) / (max - min)) * 100}%`, // Calculate the position based on value
+            transform: 'translateY(50%)', // Center the label vertically with the thumb
+            fontSize: '13px', // Font size of the label
+        }}
+    >
+        {value}
     </div>
+    <div style={{ marginTop: '120px', textAlign: 'center', fontSize: '11px', }}>
+        <b>{label}</b>
+    </div>
+</div>
   );
 };
 
+
 const TripleSliderWidget: React.FC<{
   slider1Props: SliderProps;
   slider2Props: SliderProps;
@@ -171,17 +173,25 @@ const TripleSliderWidget: React.FC<{
 
   const [startAmplitude, setStartAmplitude] = useState(slider3Props.defaultValue);
   const [endAmplitude, setEndAmplitude] = useState(50);
-  const [initialFrequency, setInitialFrequency] = useState(slider1Props.defaultValue);
-  const [endFrequency, setEndFrequency] = useState(slider2Props.defaultValue);
+  const [initialFrequency, setInitialFrequency] = useState(50);
+  const [endFrequency, setEndFrequency] = useState(50);
+  const [waveType, setWaveType] = useState('sine')
+  const waveformOptions = ["None", "Vibrato", "Tremolo", "Warble"]
+  const [textBoxValue, setTextBoxValue] = useState("2000");
+
+  const handleTextInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const newValue = e.target.value;
+    setTextBoxValue(newValue);
+  };
 
   const handleSlider1Change = (value: number) => {
     slider1Props.onChange(value);
-    setInitialFrequency(value);
+    setInitialFrequency(value/10);
   };
 
   const handleSlider2Change = (value: number) => {
     slider2Props.onChange(value);
-    setEndFrequency(value); // 
+    setEndFrequency(value/10); // 
   };
 
   const handleSlider3Change = (value: number) => {
@@ -194,6 +204,10 @@ const TripleSliderWidget: React.FC<{
     setEndAmplitude(value);
   };
 
+  const handleWaveTypeChange = (value: string) => {
+    setWaveType(value);
+  };
+
 
 
   const generateWavePath = () => {
@@ -209,9 +223,35 @@ const TripleSliderWidget: React.FC<{
       // Calculate the frequency and amplitude at the current point
       const currentFrequency = initialFrequency + (frequencyDifference * x) / waveLength;
       const currentAmplitude = startAmplitude + (amplitudeDifference * x) / waveLength;
+      const period = waveLength/currentFrequency
+      
 
       // Calculate the y-coordinate based on the current frequency and amplitude
-      const y = 50 + currentAmplitude * Math.sin(currentFrequency * x * (2 * Math.PI) / waveLength);
+      let y = 0;
+      switch (waveType) {
+        case 'sine':
+          y = 65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
+          break;
+        case 'square':
+          y = x % period < period / 2 ? 65 + currentAmplitude : 65 - currentAmplitude;
+          break;
+        case 'sawtooth':
+          y = 65 + currentAmplitude - ((x % period) / period) * (2 * currentAmplitude);
+          break;
+        case 'triangle':
+          const tPeriod = x % period;
+          y = tPeriod < period / 2
+            ? 65 + (2 * currentAmplitude / period) * tPeriod
+            : 65 - (2 * currentAmplitude / period) * (tPeriod - period / 2);
+          break;
+        case 'noisy':
+          // Generate noisy wave based on sine wave and random noise
+          // Add a random noise value to the sine wave
+          const baseWave = 65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
+          const randomNoise = Math.random() * 2 - 1; // Random noise between -1 and 1
+          y = baseWave + randomNoise * (currentAmplitude*0.3);
+          break;
+      }
       
       // Add the point to the path data
       pathData.push(`${x},${y}`);
@@ -221,37 +261,90 @@ const TripleSliderWidget: React.FC<{
     return `M${pathData.join(' ')}`;
   };
 
-  //if (!isOpen) return null;
   return (
     <div>
-      <div style={{ display: "flex", justifyContent: "flex-start", alignItems: "center" }}>
+      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'white', width: '575px', height: '150px', border: '1px solid lightgray'}}>
           {/* Vertical Slider 1 */}
-          <div style={{ marginRight: "20px" }}>
+          <div style={{marginLeft: "6px", marginRight: "20px", height: '100px', marginTop: '9px'}}>
               <Slider {...slider1Props} onChange={handleSlider1Change} vertical />
           </div>
           {/* Vertical Slider 2 */}
-          <div style={{ marginRight: "20px" }}>
+          <div style={{ marginRight: "20px", height: '100px', marginTop: '9px'}}>
               <Slider {...slider2Props} onChange={handleSlider2Change} vertical />
           </div>
           {/* Vertical Slider 3 */}
-          <div style={{ marginRight: "20px" }}>
+          <div style={{ marginRight: "20px", height: '100px', marginTop: '9px' }}>
               <Slider {...slider3Props} onChange={handleSlider3Change} vertical />
           </div>
           {/* Vertical Slider 4 */}
-          <div style={{ marginRight: "20px" }}>
+          <div style={{ marginRight: "25px", height: '100px', marginTop: '9px' }}>
               <Slider {...slider4Props} onChange={handleSlider4Change} vertical />
           </div>
+          
+
+        <div style={{ marginRight: '10px', height: '100px', fontSize: '12px' }}>
+
+            {/* waveform type selection */}
+          <label style={{ display: 'block', marginBottom: '5px', marginTop: '7px',}}>
+                <b>Waveform:</b>
+          </label>
+          <select onChange={(e) => handleWaveTypeChange(e.target.value)}>
+            <option value="sine">Sine</option>
+            <option value="square">Square</option>
+            <option value="sawtooth">Sawtooth</option>
+            <option value="triangle">Triangle</option>
+            <option value="noisy">Noisy</option>
+          </select>
+
+          {/* fx type selection */}
+
+          <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px'}}>
+                <b>Effects:</b>
+          </label>
+          <select onChange={(e) => handleWaveTypeChange(e.target.value)}>
+            <option value="sine">None</option>
+            <option value="square">Vibrato</option>
+            <option value="sawtooth">Tremelo</option>
+            <option value="triangle">Warble</option>
+          </select>
+
+          {/* Duration selctor */}
+
+          <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px' }}>
+              <b>Duration(ms):</b>
+          </label>
+          {/* Input field with associated datalist */}
+          <input
+            type="text"
+            value={textBoxValue}
+            onChange={handleTextInputChange} // Handle the selected or typed-in value
+            defaultValue="2000" 
+            style={{ width: '75px' }}
+          />
+
+        </div>
           {/* Waveform box */}
-          <div style={{ width: '200px', height: '100px', border: '1px solid black' }}>
+          <div style={{ width: '200px', height: '130px', backgroundColor: 'linen', marginTop: '9px', marginLeft: '5px'}}>
               <svg width="100%" height="100%">
                   <path d={generateWavePath()} stroke="black" fill="none" />
+                  <line
+                      x1="0%" // Start of the line
+                      y1="50%" // Vertically center the line
+                      x2="100%" // End of the line
+                      y2="50%" // Keep the line horizontal
+                      stroke="gray" // Line color
+                      strokeWidth="0.5" // Line thickness
+                  />
               </svg>
           </div>
-      </div>
     </div>
+</div>
+
   );
 };
 
+
+
 export const SoundComponent = ({
   props,
   view,

From 9cac1832c7e7dad1a44eef388894b3b24fdfa6ae Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Sat, 20 Apr 2024 21:24:08 +0100
Subject: [PATCH 095/106] minor UI changes

---
 src/editor/codemirror/helper-widgets/soundWidget.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index b34fbadaf..da2bf4356 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -140,7 +140,7 @@ const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = (
             width: '115px', // Width of the slider
             height: '40px', // Height of the slider
             transform: 'rotate(-90deg)', // Rotate the slider to vertical orientation
-            accentColor: colour, 
+            accentColor: colour,
             bottom: '0%',
         }}
     />
@@ -263,7 +263,7 @@ const TripleSliderWidget: React.FC<{
 
   return (
     <div>
-      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'white', width: '575px', height: '150px', border: '1px solid lightgray'}}>
+      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'snow', width: '575px', height: '150px', border: '1px solid lightgray'}}>
           {/* Vertical Slider 1 */}
           <div style={{marginLeft: "6px", marginRight: "20px", height: '100px', marginTop: '9px'}}>
               <Slider {...slider1Props} onChange={handleSlider1Change} vertical />

From 236ae83914a183ad930e8b19a9578ebbfaecb06e Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 21 Apr 2024 11:52:17 +0100
Subject: [PATCH 096/106] ye

---
 src/editor/codemirror/helper-widgets/showImageWidget.tsx | 5 +++--
 src/editor/codemirror/helper-widgets/widgetArgParser.tsx | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index 071e4d6d3..c7d8d8201 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -153,7 +153,7 @@ export const MicrobitMultiplePixelComponent = ({
           to: ranges[0].to,
           insert: insertion,
         },
-        effects: [openWidgetEffect.of(to)],
+        effects: [openWidgetEffect.of(insertion.length + from + 2)],
       });
     } else {
       view.dispatch({
@@ -211,7 +211,7 @@ const parseArgs = (args: string[]): number[][] => {
 };
 
 function pixelsToString(pixels: number[][]): string {
-  let outputString = "";
+  let outputString = '"';
   for (let y = 0; y < 5; y++) {
     for (let x = 0; x < 5; x++) {
       outputString += pixels[y][x].toString();
@@ -219,5 +219,6 @@ function pixelsToString(pixels: number[][]): string {
     outputString += ":";
   }
   outputString = outputString.slice(0, -1);
+  outputString += '"';
   return outputString;
 }
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 371b0f2b4..6dbcf922c 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -109,7 +109,7 @@ export function ValidateComponentArgs(
       }
       // If some arguments are not numbers or empty, don't open
       for (let i = 0; i < args.length; i++) {
-        if (types[i] !== "Number" && args[i] != ",") {
+        if (types[i] !== "Number" && args[i] !== ",") {
           return false;
         }
       }

From 372763863357cf104529d0cea40dfbf28f613051 Mon Sep 17 00:00:00 2001
From: Marta Religa <marta.religa@cs.ox.ac.uk>
Date: Sun, 21 Apr 2024 12:03:20 +0100
Subject: [PATCH 097/106] .

---
 src/editor/codemirror/helper-widgets/showImageWidget.tsx | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/showImageWidget.tsx b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
index c7d8d8201..484747ebb 100644
--- a/src/editor/codemirror/helper-widgets/showImageWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/showImageWidget.tsx
@@ -129,8 +129,6 @@ export const MicrobitMultiplePixelComponent = ({
   let from = props.from;
   let to = props.to;
 
-  console.log(args);
-  console.log(types);
   const initialSelectedPixels = parseArgs(args);
   const [selectedPixels, setSelectedPixels] = useState<number[][]>(
     initialSelectedPixels
@@ -193,7 +191,7 @@ const parseArgs = (args: string[]): number[][] => {
   if (args.length !== 1) {
     return defaultPixels;
   }
-  const argString = args[0];
+  const argString = args[0].replace(/"/g, "");
   const rows = argString.split(":");
   if (rows.length !== 5) {
     return defaultPixels;

From aa5fa41b8ef4d4583b231e4cb40707db37c48f15 Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Sun, 21 Apr 2024 12:19:22 +0100
Subject: [PATCH 098/106] Props to triple widget

---
 .../codemirror/helper-widgets/soundWidget.tsx | 38 +++++++++----------
 1 file changed, 18 insertions(+), 20 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index da2bf4356..8aa7756ad 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -53,12 +53,12 @@ const endFrequencySliderProps: SliderProps = {
     console.log("Slider value changed:", value);
   },
   sliderStyle: {
-    width: "100%", // Adjust the width of the slider
-    height: "100px", // Adjust the height of the slider
-    backgroundColor: "lightgray", // Change the background color of the slider
-    borderRadius: "10px", // Apply rounded corners to the slider track
-    border: "none", // Remove the border of the slider track
-    outline: "none", // Remove the outline when focused
+    width: "100%", 
+    height: "100px", 
+    backgroundColor: "lightgray", 
+    borderRadius: "10px", 
+    border: "none", 
+    outline: "none", 
   },
   label: "End Freq",
   vertical: true,
@@ -168,8 +168,15 @@ const TripleSliderWidget: React.FC<{
   slider2Props: SliderProps;
   slider3Props: SliderProps;
   slider4Props: SliderProps;
-  isOpen: boolean;
-}> = ({ slider1Props, slider2Props, slider3Props, slider4Props, isOpen }) => {
+  props: WidgetProps;
+  view: EditorView;
+}> = ({ slider1Props, slider2Props, slider3Props, slider4Props, props}) => {
+
+  let args = props.args;
+  let ranges = props.ranges;
+  let types = props.types;
+  let from = props.from;
+  let to = props.to;
 
   const [startAmplitude, setStartAmplitude] = useState(slider3Props.defaultValue);
   const [endAmplitude, setEndAmplitude] = useState(50);
@@ -352,23 +359,13 @@ export const SoundComponent = ({
   props: WidgetProps;
   view: EditorView;
 }) => {
-  let args = props.args;
-  //let ranges = props.ranges;
-  let types = props.types;
-  let from = props.from;
-  let to = props.to;
+
   //for future reference add a aclose button
   const handleCloseClick = () => {
     view.dispatch({
       effects: [openWidgetEffect.of(-1)],
     });
   };
-  const [isSoundEditorOpen, setIsSoundEditorOpen] = useState(false);
-  const buttonLabel = isSoundEditorOpen ? "Close" : "Open";
-  const handleButtonClick = () => {
-    setIsSoundEditorOpen(!isSoundEditorOpen);
-    // Toggle the state to open/close the DualSlider
-  };
   /*
   view.dispatch({
     changes: {
@@ -390,7 +387,8 @@ export const SoundComponent = ({
         slider2Props={endFrequencySliderProps}
         slider3Props={startVolProps}
         slider4Props={endVolProps}
-        isOpen={isSoundEditorOpen}
+        props={props}
+        view={view}
       />
     </HStack>
   );

From d5479dc692e31072fcbf7a1e602d231c0f1aeaaf Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 23 Apr 2024 14:03:45 +0100
Subject: [PATCH 099/106] eq check for Widget

---
 .../codemirror/helper-widgets/reactWidgetExtension.tsx       | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 97a6dfea9..9936cbb84 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -40,6 +40,11 @@ class Widget extends WidgetType {
     super();
   }
 
+  eq(other: WidgetType): boolean {
+    const them = other as Widget;
+    return them.component === this.component && them.props.to === this.props.to && them.inline === this.inline;
+  }
+
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 

From f67a3d290dee1d1846ecda44aba1e885e05285c5 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 23 Apr 2024 14:45:09 +0100
Subject: [PATCH 100/106] option to have different open buttons

---
 .../helper-widgets/widgetArgParser.tsx        | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 6dbcf922c..e4238ca8c 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -8,6 +8,7 @@ import { SoundComponent } from "./soundWidget";
 export interface CompProps {
   comp: React.ComponentType<any>;
   props: WidgetProps;
+  open: boolean
 }
 
 export function createWidget(
@@ -47,6 +48,7 @@ export function createWidget(
         from: node.from,
         to: node.to,
       },
+      open: OpenButtonDesign(component, args, types)
     };
   }
   return null;
@@ -94,6 +96,24 @@ function getTypes(nodes: SyntaxNode[]): string[] {
   return types;
 }
 
+function OpenButtonDesign(
+  name: React.ComponentType<any>,
+  args: string[],
+  types: string[]
+): boolean {
+  switch (name) {
+    case MicrobitMultiplePixelComponent:
+      return true;
+    case MicrobitSinglePixelComponent:
+      return true;
+    case SoundComponent:
+      return true;
+    default:
+      // shouldnt be called so just null
+      return false;
+  }
+}
+
 export function ValidateComponentArgs(
   name: React.ComponentType<any>,
   args: string[],

From 0221179d049205b38b19f76fed4865e3ff096253 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 23 Apr 2024 14:53:27 +0100
Subject: [PATCH 101/106] different open button displays backend

---
 .../helper-widgets/reactWidgetExtension.tsx         |  4 +++-
 .../codemirror/helper-widgets/widgetArgParser.tsx   | 13 +++++++------
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 9936cbb84..1b053b52c 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -34,6 +34,7 @@ class Widget extends WidgetType {
   constructor(
     private component: React.ComponentType<any>,
     private props: WidgetProps,
+    private open: React.ComponentType<any>,
     private inline: boolean,
     private createPortal: PortalFactory
   ) {
@@ -53,7 +54,7 @@ class Widget extends WidgetType {
         dom.style.display = "inline-block"; // want it inline for the open-close widget
         this.portalCleanup = this.createPortal(
           dom,
-          <OpenReactComponent loc={this.props.to} view={view} />
+          <this.open loc={this.props.to} view={view} />
         );
       }
     } else
@@ -94,6 +95,7 @@ export const reactWidgetExtension = (
               widget: new Widget(
                 widget.comp,
                 widget.props,
+                widget.open,
                 widget.props.to !== openWidgetLoc,
                 createPortal
               ),
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index e4238ca8c..20a911282 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -4,11 +4,12 @@ import { WidgetProps } from "./reactWidgetExtension";
 import { MicrobitSinglePixelComponent } from "./setPixelWidget";
 import { MicrobitMultiplePixelComponent } from "./showImageWidget";
 import { SoundComponent } from "./soundWidget";
+import { OpenReactComponent } from "./openWidgets";
 
 export interface CompProps {
   comp: React.ComponentType<any>;
   props: WidgetProps;
-  open: boolean
+  open: React.ComponentType<any>
 }
 
 export function createWidget(
@@ -100,17 +101,17 @@ function OpenButtonDesign(
   name: React.ComponentType<any>,
   args: string[],
   types: string[]
-): boolean {
+): React.ComponentType<any> {
   switch (name) {
     case MicrobitMultiplePixelComponent:
-      return true;
+      return OpenReactComponent;
     case MicrobitSinglePixelComponent:
-      return true;
+      return OpenReactComponent;
     case SoundComponent:
-      return true;
+      return OpenReactComponent;
     default:
       // shouldnt be called so just null
-      return false;
+      return OpenReactComponent;
   }
 }
 

From ff5a9ae6c59ee22c10211014460ebb001ea507c9 Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Tue, 23 Apr 2024 17:13:00 +0100
Subject: [PATCH 102/106] Open button now customised to component. Fixed line
 indent issue. Messed around with set pixel UI

---
 .../codemirror/helper-widgets/openWidgets.tsx | 66 +++++++++++++++++--
 .../helper-widgets/setPixelWidget.tsx         |  8 +--
 .../codemirror/helper-widgets/soundWidget.tsx | 15 +++--
 .../helper-widgets/widgetArgParser.tsx        |  4 +-
 4 files changed, 77 insertions(+), 16 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/openWidgets.tsx b/src/editor/codemirror/helper-widgets/openWidgets.tsx
index f8df3e5b4..fcb392471 100644
--- a/src/editor/codemirror/helper-widgets/openWidgets.tsx
+++ b/src/editor/codemirror/helper-widgets/openWidgets.tsx
@@ -1,4 +1,4 @@
-import { Button, HStack } from "@chakra-ui/react";
+import { Button, Center, HStack } from "@chakra-ui/react";
 import { StateEffect } from "@codemirror/state";
 import { EditorView } from "@codemirror/view";
 import { useCallback } from "react";
@@ -17,8 +17,66 @@ export const OpenReactComponent = ({
     });
   }, [loc, view]);
   return (
-    <HStack fontFamily="body" spacing={5} py={3}>
-      <Button onClick={handleClick}>Open</Button>
-    </HStack>
+      <Button onClick={handleClick} size="xs">Open</Button>
   );
 };
+
+
+function createSoundWavePath(): string {
+  let pathData = 'M0,12'; 
+
+  const totalPoints = 18; 
+
+    
+    const stepSize = 24 / totalPoints;
+
+    for (let i = 0; i < totalPoints; i++) {
+      const x = i * stepSize;
+      const angle = (x / totalPoints) * 3 * Math.PI;
+
+      const heightVariation = Math.cos(angle) * 6;
+      const y1 = 12 + heightVariation; 
+      const y2 = 12 - heightVariation; 
+      
+      pathData += ` M${x},${y1} L${x},${y2}`;
+  }
+
+  return pathData;
+}
+
+export const OpenSoundComponent = ({
+  loc,
+  view,
+}: {
+  loc: number;
+  view: EditorView;
+}) => {
+
+  
+
+  const handleClick = useCallback(() => {
+    view.dispatch({
+      effects: [openWidgetEffect.of(loc)],
+    });
+  }, [loc, view]);
+
+  const soundWavePath = createSoundWavePath();
+
+  return (
+      <Button onClick={handleClick} size="sm" height="25px" marginBottom="3px" marginLeft="5px" style={{ padding: '3px 3px' }}>
+          <svg
+              width="20" 
+              height="18" 
+              viewBox="0 0 24 24"
+              fill="none"
+          >
+              <path
+                  d={soundWavePath}
+                  stroke="green" 
+                  strokeWidth="1" 
+                  fill="none" 
+              />
+          </svg>
+      </Button>
+  );
+};
\ No newline at end of file
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 01eaabb90..d389ad563 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -22,7 +22,6 @@ interface MicrobitSinglePixelGridProps {
   initialPixel: Pixel | null;
   onCloseClick: () => void;
 }
-
 const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   onPixelClick,
   initialPixel,
@@ -67,11 +66,10 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
           {[...Array(5)].map((_, gridY) => (
             <Box key={y} display="flex">
               {[...Array(5)].map((_, gridX) => (
-                <Box key={x} display="flex" mr="2px">
+                <Box key={x} display="flex" mr="0px">
                   <Button
-                    size="xs"
-                    h="15px"
-                    w="15px"
+                    height="30px"
+                    width="30x"
                     p={0}
                     borderRadius={0}
                     bgColor={
diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index 8aa7756ad..76405569a 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -186,6 +186,14 @@ const TripleSliderWidget: React.FC<{
   const waveformOptions = ["None", "Vibrato", "Tremolo", "Warble"]
   const [textBoxValue, setTextBoxValue] = useState("2000");
 
+
+
+  for (let i = 0; i < args.length; i++) {
+    let arg = args[i];
+    console.log("arg: ", arg);
+  };
+
+
   const handleTextInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
     const newValue = e.target.value;
     setTextBoxValue(newValue);
@@ -221,13 +229,11 @@ const TripleSliderWidget: React.FC<{
     const waveLength = 400; // Width of the box
     const pathData = [];
     
-    // Calculate the change in frequency and amplitude over the length of the waveform
     const frequencyDifference = endFrequency - initialFrequency;
     const amplitudeDifference = endAmplitude - startAmplitude;
 
     // Loop through the wave's width to generate the path
     for (let x = 0; x <= waveLength; x++) {
-      // Calculate the frequency and amplitude at the current point
       const currentFrequency = initialFrequency + (frequencyDifference * x) / waveLength;
       const currentAmplitude = startAmplitude + (amplitudeDifference * x) / waveLength;
       const period = waveLength/currentFrequency
@@ -253,9 +259,8 @@ const TripleSliderWidget: React.FC<{
           break;
         case 'noisy':
           // Generate noisy wave based on sine wave and random noise
-          // Add a random noise value to the sine wave
           const baseWave = 65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
-          const randomNoise = Math.random() * 2 - 1; // Random noise between -1 and 1
+          const randomNoise = Math.random() * 2 - 1; 
           y = baseWave + randomNoise * (currentAmplitude*0.3);
           break;
       }
@@ -270,7 +275,7 @@ const TripleSliderWidget: React.FC<{
 
   return (
     <div>
-      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'snow', width: '575px', height: '150px', border: '1px solid lightgray'}}>
+      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'snow', width: '575px', height: '150px', border: '1px solid lightgray', boxShadow: '0 0 10px 5px rgba(173, 216, 230, 0.7)'}}>
           {/* Vertical Slider 1 */}
           <div style={{marginLeft: "6px", marginRight: "20px", height: '100px', marginTop: '9px'}}>
               <Slider {...slider1Props} onChange={handleSlider1Change} vertical />
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index 20a911282..db6da1964 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -4,7 +4,7 @@ import { WidgetProps } from "./reactWidgetExtension";
 import { MicrobitSinglePixelComponent } from "./setPixelWidget";
 import { MicrobitMultiplePixelComponent } from "./showImageWidget";
 import { SoundComponent } from "./soundWidget";
-import { OpenReactComponent } from "./openWidgets";
+import { OpenReactComponent, OpenSoundComponent } from "./openWidgets";
 
 export interface CompProps {
   comp: React.ComponentType<any>;
@@ -108,7 +108,7 @@ function OpenButtonDesign(
     case MicrobitSinglePixelComponent:
       return OpenReactComponent;
     case SoundComponent:
-      return OpenReactComponent;
+      return OpenSoundComponent;
     default:
       // shouldnt be called so just null
       return OpenReactComponent;

From a76eb56300a370135741a0b9af1b2561d78399ff Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Tue, 23 Apr 2024 18:23:55 +0100
Subject: [PATCH 103/106] Minor changes to unify look

---
 .../helper-widgets/setPixelWidget.tsx         | 33 +++++++++++--------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index d389ad563..7769e701b 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -45,31 +45,36 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
   };
 
   return (
+    <div>
+    <Box ml="10px" style={{ marginRight: "4px" }}>
+        <Button size="xs" onClick={onCloseClick} bg="white">
+          X
+        </Button>
+    </Box>
     <Box // TODO: copy to allow other widgets to access bg and close
       display="flex"
       flexDirection="row"
       justifyContent="flex-start"
-      bg="lightgray"
+      width="250px"
+      background="snon"
+      border='1px solid lightgray'
+      boxShadow='0 0 10px 5px rgba(173, 216, 230, 0.7)'
     >
-      <Box ml="10px" style={{ marginRight: "4px" }}>
-        <Button size="xs" onClick={onCloseClick} bg="white">
-          X
-        </Button>
-      </Box>
       <Box>
         <Box
-          bg="black"
+          bg="white"
           p="10px"
-          borderRadius="5px"
-          style={{ marginTop: "15px" }}
+          borderRadius="0px"
+          border="1px solid black"
+          style={{ marginLeft: "15px", marginTop: "15px", marginBottom: "15px" }}
         >
           {[...Array(5)].map((_, gridY) => (
             <Box key={y} display="flex">
               {[...Array(5)].map((_, gridX) => (
                 <Box key={x} display="flex" mr="0px">
                   <Button
-                    height="30px"
-                    width="30x"
+                    height="32px"
+                    width="30px"
                     p={0}
                     borderRadius={0}
                     bgColor={
@@ -79,8 +84,8 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
                     }
                     border={
                       gridX === x && gridY === y
-                        ? "2px solid white"
-                        : "0.5px solid white"
+                        ? "2px solid black"
+                        : "1px solid black"
                     }
                     _hover={{
                       bgColor:
@@ -103,6 +108,7 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
           min={0}
           max={9}
           step={1}
+          height="182px"
           orientation="vertical"
           _focus={{ boxShadow: "none" }}
           _active={{ bgColor: "transparent" }}
@@ -115,6 +121,7 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
         </Slider>
       </Box>
     </Box>
+    </div>
   );
 };
 

From f6795a8a4dcbd5740b45be7e04029f0beb529c1c Mon Sep 17 00:00:00 2001
From: Aryan <aryan.rastogi1@yahoo.co.uk>
Date: Wed, 24 Apr 2024 21:04:32 +0100
Subject: [PATCH 104/106] Sound widget now updates parameters in editor. Super
 glitchy though.

---
 .../codemirror/helper-widgets/soundWidget.tsx | 318 +++++++++++-------
 1 file changed, 204 insertions(+), 114 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index 76405569a..1fea4bdfe 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -10,6 +10,10 @@ import {
 import { EditorView } from "@codemirror/view";
 import { WidgetProps } from "./reactWidgetExtension";
 import { openWidgetEffect } from "./openWidgets";
+import { zIndexAboveDialogs } from "../../../common/zIndex";
+import { start } from "repl";
+
+type FixedLengthArray = [number, number, number, number, number, string, string];
 
 interface SliderProps {
   min: number;
@@ -25,7 +29,7 @@ interface SliderProps {
 
 const startVolProps: SliderProps = {
   min: 0,
-  max: 100,
+  max: 255,
   step: 1,
   defaultValue: 50,
   onChange: (value) => {
@@ -46,19 +50,19 @@ const startVolProps: SliderProps = {
 
 const endFrequencySliderProps: SliderProps = {
   min: 0,
-  max: 999,
+  max: 9999,
   step: 1,
-  defaultValue: 500,
+  defaultValue: 5000,
   onChange: (value) => {
     console.log("Slider value changed:", value);
   },
   sliderStyle: {
-    width: "100%", 
-    height: "100px", 
-    backgroundColor: "lightgray", 
-    borderRadius: "10px", 
-    border: "none", 
-    outline: "none", 
+    width: "100%",
+    height: "100px",
+    backgroundColor: "lightgray",
+    borderRadius: "10px",
+    border: "none",
+    outline: "none",
   },
   label: "End Freq",
   vertical: true,
@@ -67,9 +71,9 @@ const endFrequencySliderProps: SliderProps = {
 
 const startFrequencySliderProps: SliderProps = {
   min: 0,
-  max: 999,
+  max: 9999,
   step: 1,
-  defaultValue: 500,
+  defaultValue: 5000,
   onChange: (value) => {
     console.log("Slider value changed:", value);
   },
@@ -88,7 +92,7 @@ const startFrequencySliderProps: SliderProps = {
 
 const endVolProps: SliderProps = {
   min: 0,
-  max: 100,
+  max: 255,
   step: 1,
   defaultValue: 50,
   onChange: (value) => {
@@ -128,7 +132,7 @@ const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = (
 
   return (
     <div style={{ position: 'relative', height: '80px', width: '45px', display: "flex", flexDirection: "column", alignItems: "center" }}>
-    <input
+      <input
         type="range"
         min={min}
         max={max}
@@ -136,41 +140,41 @@ const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = (
         value={value}
         onChange={handleChange}
         style={{
-            position: 'absolute',
-            width: '115px', // Width of the slider
-            height: '40px', // Height of the slider
-            transform: 'rotate(-90deg)', // Rotate the slider to vertical orientation
-            accentColor: colour,
-            bottom: '0%',
+          position: 'absolute',
+          width: '115px', // Width of the slider
+          height: '40px', // Height of the slider
+          transform: 'rotate(-90deg)', // Rotate the slider to vertical orientation
+          accentColor: colour,
+          bottom: '0%',
         }}
-    />
-    <div
+      />
+      <div
         style={{
-            position: 'absolute',
-            left: 'calc(100% - 15px)', // Position the label to the right of the slider
-            bottom: `${((value - min) / (max - min)) * 100}%`, // Calculate the position based on value
-            transform: 'translateY(50%)', // Center the label vertically with the thumb
-            fontSize: '13px', // Font size of the label
+          position: 'absolute',
+          left: 'calc(100% - 15px)', // Position the label to the right of the slider
+          bottom: `${((value - min) / (max - min)) * 100}%`, // Calculate the position based on value
+          transform: 'translateY(50%)', // Center the label vertically with the thumb
+          fontSize: '13px', // Font size of the label
         }}
-    >
+      >
         {value}
-    </div>
-    <div style={{ marginTop: '120px', textAlign: 'center', fontSize: '11px', }}>
+      </div>
+      <div style={{ marginTop: '120px', textAlign: 'center', fontSize: '11px', }}>
         <b>{label}</b>
+      </div>
     </div>
-</div>
   );
 };
 
 
 const TripleSliderWidget: React.FC<{
-  slider1Props: SliderProps;
-  slider2Props: SliderProps;
-  slider3Props: SliderProps;
-  slider4Props: SliderProps;
+  freqStartProps: SliderProps;
+  freqEndProps: SliderProps;
+  volStartProps: SliderProps;
+  volEndprops: SliderProps;
   props: WidgetProps;
   view: EditorView;
-}> = ({ slider1Props, slider2Props, slider3Props, slider4Props, props}) => {
+}> = ({ freqStartProps, freqEndProps, volStartProps, volEndprops, props, view}) => {
 
   let args = props.args;
   let ranges = props.ranges;
@@ -178,45 +182,94 @@ const TripleSliderWidget: React.FC<{
   let from = props.from;
   let to = props.to;
 
-  const [startAmplitude, setStartAmplitude] = useState(slider3Props.defaultValue);
-  const [endAmplitude, setEndAmplitude] = useState(50);
-  const [initialFrequency, setInitialFrequency] = useState(50);
-  const [endFrequency, setEndFrequency] = useState(50);
-  const [waveType, setWaveType] = useState('sine')
-  const waveformOptions = ["None", "Vibrato", "Tremolo", "Warble"]
-  const [textBoxValue, setTextBoxValue] = useState("2000");
-
+  //parse args
 
-
-  for (let i = 0; i < args.length; i++) {
+  let argsToBeUsed: FixedLengthArray = [200, 500, 2000, 50, 50, "sine", "None"] // default args
+  let count = 0
+  for (let i = 2; i < args.length; i += 3) { //Update default args with user args where they exist
+    argsToBeUsed[count] = args[i]
     let arg = args[i];
     console.log("arg: ", arg);
+    count += 1;
+  };
+
+  console.log("args", argsToBeUsed)
+
+  const [initialFrequency, setInitialFrequency] = useState(Math.min(argsToBeUsed[0], 9999));
+  freqStartProps.defaultValue = initialFrequency
+
+  const [endFrequency, setEndFrequency] = useState(Math.min(argsToBeUsed[1], 9999));
+  freqEndProps.defaultValue = endFrequency
+
+
+  const [startAmplitude, setStartAmplitude] = useState(Math.min(argsToBeUsed[3], 255));
+  volStartProps.defaultValue = startAmplitude
+
+  const [endAmplitude, setEndAmplitude] = useState(Math.min(argsToBeUsed[4], 9999));
+  volEndprops.defaultValue = endAmplitude
+
+  
+  const [waveType, setWaveType] = useState("sine")
+
+  const waveformOptions = ["None", "Vibrato", "Tremolo", "Warble"]
+  const [textBoxValue, setTextBoxValue] = useState(Number(argsToBeUsed[2]));
+
+
+  const updateView = () => {
+    let insertion = statesToString(initialFrequency, endFrequency, textBoxValue, startAmplitude, endAmplitude);
+    console.log(insertion);
+    if (ranges.length === 1) {
+      view.dispatch({
+        changes: {
+          from: ranges[0].from,
+          to: ranges[0].to,
+          insert: insertion,
+        },
+        effects: [openWidgetEffect.of(insertion.length + from + 2)],
+      });
+    } else {
+      view.dispatch({
+        changes: [
+          {
+            from: from + 1,
+            to: to - 1,
+            insert: insertion,
+          },
+        ],
+        effects: [openWidgetEffect.of(insertion.length + from + 2)],
+      });
+    }
   };
 
 
   const handleTextInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
     const newValue = e.target.value;
-    setTextBoxValue(newValue);
+    setTextBoxValue(Number(newValue));
+    updateView();
   };
 
   const handleSlider1Change = (value: number) => {
-    slider1Props.onChange(value);
-    setInitialFrequency(value/10);
+    freqStartProps.onChange(value);
+    setInitialFrequency(value);
+    updateView();
   };
 
   const handleSlider2Change = (value: number) => {
-    slider2Props.onChange(value);
-    setEndFrequency(value/10); // 
+    freqEndProps.onChange(value);
+    setEndFrequency(value); // 
+    updateView();
   };
 
   const handleSlider3Change = (value: number) => {
-    slider1Props.onChange(value);
+    freqStartProps.onChange(value);
     setStartAmplitude(value);
+    updateView();
   };
 
   const handleSlider4Change = (value: number) => {
-    slider1Props.onChange(value);
+    freqStartProps.onChange(value);
     setEndAmplitude(value);
+    updateView();
   };
 
   const handleWaveTypeChange = (value: string) => {
@@ -228,16 +281,16 @@ const TripleSliderWidget: React.FC<{
   const generateWavePath = () => {
     const waveLength = 400; // Width of the box
     const pathData = [];
-    
+
     const frequencyDifference = endFrequency - initialFrequency;
     const amplitudeDifference = endAmplitude - startAmplitude;
 
     // Loop through the wave's width to generate the path
     for (let x = 0; x <= waveLength; x++) {
-      const currentFrequency = initialFrequency + (frequencyDifference * x) / waveLength;
-      const currentAmplitude = startAmplitude + (amplitudeDifference * x) / waveLength;
-      const period = waveLength/currentFrequency
-      
+      const currentFrequency = (initialFrequency + (frequencyDifference * x) / waveLength)/100;
+      const currentAmplitude = (startAmplitude + (amplitudeDifference * x) / waveLength)/2.2;
+      const period = waveLength / currentFrequency
+
 
       // Calculate the y-coordinate based on the current frequency and amplitude
       let y = 0;
@@ -260,11 +313,11 @@ const TripleSliderWidget: React.FC<{
         case 'noisy':
           // Generate noisy wave based on sine wave and random noise
           const baseWave = 65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
-          const randomNoise = Math.random() * 2 - 1; 
-          y = baseWave + randomNoise * (currentAmplitude*0.3);
+          const randomNoise = Math.random() * 2 - 1;
+          y = baseWave + randomNoise * (currentAmplitude * 0.3);
           break;
       }
-      
+
       // Add the point to the path data
       pathData.push(`${x},${y}`);
     }
@@ -275,30 +328,30 @@ const TripleSliderWidget: React.FC<{
 
   return (
     <div>
-      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'snow', width: '575px', height: '150px', border: '1px solid lightgray', boxShadow: '0 0 10px 5px rgba(173, 216, 230, 0.7)'}}>
-          {/* Vertical Slider 1 */}
-          <div style={{marginLeft: "6px", marginRight: "20px", height: '100px', marginTop: '9px'}}>
-              <Slider {...slider1Props} onChange={handleSlider1Change} vertical />
-          </div>
-          {/* Vertical Slider 2 */}
-          <div style={{ marginRight: "20px", height: '100px', marginTop: '9px'}}>
-              <Slider {...slider2Props} onChange={handleSlider2Change} vertical />
-          </div>
-          {/* Vertical Slider 3 */}
-          <div style={{ marginRight: "20px", height: '100px', marginTop: '9px' }}>
-              <Slider {...slider3Props} onChange={handleSlider3Change} vertical />
-          </div>
-          {/* Vertical Slider 4 */}
-          <div style={{ marginRight: "25px", height: '100px', marginTop: '9px' }}>
-              <Slider {...slider4Props} onChange={handleSlider4Change} vertical />
-          </div>
-          
+      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'snow', width: '575px', height: '150px', border: '1px solid lightgray', boxShadow: '0 0 10px 5px rgba(173, 216, 230, 0.7)', zIndex: 10 }}>
+        {/* Vertical Slider 1 */}
+        <div style={{ marginLeft: "6px", marginRight: "20px", height: '100px', marginTop: '9px' }}>
+          <Slider {...freqStartProps} onChange={handleSlider1Change} vertical />
+        </div>
+        {/* Vertical Slider 2 */}
+        <div style={{ marginRight: "20px", height: '100px', marginTop: '9px' }}>
+          <Slider {...freqEndProps} onChange={handleSlider2Change} vertical />
+        </div>
+        {/* Vertical Slider 3 */}
+        <div style={{ marginRight: "20px", height: '100px', marginTop: '9px' }}>
+          <Slider {...volStartProps} onChange={handleSlider3Change} vertical />
+        </div>
+        {/* Vertical Slider 4 */}
+        <div style={{ marginRight: "25px", height: '100px', marginTop: '9px' }}>
+          <Slider {...volEndprops} onChange={handleSlider4Change} vertical />
+        </div>
+
 
         <div style={{ marginRight: '10px', height: '100px', fontSize: '12px' }}>
 
-            {/* waveform type selection */}
-          <label style={{ display: 'block', marginBottom: '5px', marginTop: '7px',}}>
-                <b>Waveform:</b>
+          {/* waveform type selection */}
+          <label style={{ display: 'block', marginBottom: '5px', marginTop: '7px', }}>
+            <b>Waveform:</b>
           </label>
           <select onChange={(e) => handleWaveTypeChange(e.target.value)}>
             <option value="sine">Sine</option>
@@ -310,8 +363,8 @@ const TripleSliderWidget: React.FC<{
 
           {/* fx type selection */}
 
-          <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px'}}>
-                <b>Effects:</b>
+          <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px' }}>
+            <b>Effects:</b>
           </label>
           <select onChange={(e) => handleWaveTypeChange(e.target.value)}>
             <option value="sine">None</option>
@@ -323,34 +376,34 @@ const TripleSliderWidget: React.FC<{
           {/* Duration selctor */}
 
           <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px' }}>
-              <b>Duration(ms):</b>
+            <b>Duration(ms):</b>
           </label>
           {/* Input field with associated datalist */}
           <input
             type="text"
             value={textBoxValue}
             onChange={handleTextInputChange} // Handle the selected or typed-in value
-            defaultValue="2000" 
+            defaultValue="2000"
             style={{ width: '75px' }}
           />
 
         </div>
-          {/* Waveform box */}
-          <div style={{ width: '200px', height: '130px', backgroundColor: 'linen', marginTop: '9px', marginLeft: '5px'}}>
-              <svg width="100%" height="100%">
-                  <path d={generateWavePath()} stroke="black" fill="none" />
-                  <line
-                      x1="0%" // Start of the line
-                      y1="50%" // Vertically center the line
-                      x2="100%" // End of the line
-                      y2="50%" // Keep the line horizontal
-                      stroke="gray" // Line color
-                      strokeWidth="0.5" // Line thickness
-                  />
-              </svg>
-          </div>
+        {/* Waveform box */}
+        <div style={{ width: '200px', height: '130px', backgroundColor: 'linen', marginTop: '9px', marginLeft: '5px' }}>
+          <svg width="100%" height="100%">
+            <path d={generateWavePath()} stroke="black" fill="none" />
+            <line
+              x1="0%" // Start of the line
+              y1="50%" // Vertically center the line
+              x2="100%" // End of the line
+              y2="50%" // Keep the line horizontal
+              stroke="gray" // Line color
+              strokeWidth="0.5" // Line thickness
+            />
+          </svg>
+        </div>
+      </div>
     </div>
-</div>
 
   );
 };
@@ -365,36 +418,73 @@ export const SoundComponent = ({
   view: EditorView;
 }) => {
 
+  let args = props.args;
+  let ranges = props.ranges;
+  let types = props.types;
+  let from = props.from;
+  let to = props.to
+
   //for future reference add a aclose button
   const handleCloseClick = () => {
     view.dispatch({
       effects: [openWidgetEffect.of(-1)],
     });
   };
-  /*
-  view.dispatch({
-    changes: {
-      from: from,
-      to: to,
-      insert:  //something from state of component`,
-    },
-    */
-// <Button onClick={handleButtonClick}>{buttonLabel} Sound Editor</Button>
+  
+  const updateView = () => {
+    let insertion = "test";
+    console.log(insertion);
+    if (ranges.length === 1) {
+      view.dispatch({
+        changes: {
+          from: ranges[0].from,
+          to: ranges[0].to,
+          insert: insertion,
+        },
+        effects: [openWidgetEffect.of(insertion.length + from + 2)],
+      });
+    } else {
+      view.dispatch({
+        changes: [
+          {
+            from: from + 1,
+            to: to - 1,
+            insert: insertion,
+          },
+        ],
+        effects: [openWidgetEffect.of(insertion.length + from + 2)],
+      });
+    }
+  };
+  
   return (
-    <HStack fontFamily="body" spacing={5} py={3}>
+    <HStack fontFamily="body" spacing={5} py={3} zIndex={10}>
       <Box ml="10px" style={{ marginRight: "4px" }}>
         <Button size="xs" onClick={handleCloseClick} bg="white">
           X
         </Button>
       </Box>
       <TripleSliderWidget
-        slider1Props={startFrequencySliderProps}
-        slider2Props={endFrequencySliderProps}
-        slider3Props={startVolProps}
-        slider4Props={endVolProps}
+        freqStartProps={startFrequencySliderProps}
+        freqEndProps={endFrequencySliderProps}
+        volStartProps={startVolProps}
+        volEndprops={endVolProps}
         props={props}
         view={view}
       />
     </HStack>
   );
 };
+
+//(startFreq: number, endFreq: Number, duration: Number, startVol: number, endVol: Number, waveform: string, fx: string)
+
+function statesToString(startFreq: number, endFreq: Number, duration: Number, startVol: number, endVol: Number): string {
+  return `\n`
+  + `        freq_start=${startFreq},\n`
+  + `        freq_end=${endFreq},\n`
+  + `        duration=${duration},\n`
+  + `        vol_start=${startVol},\n`
+  + `        vol_end=${endVol},\n`
+  + `        waveform=SoundEffect.FX_WARBLE,\n`
+  + `        fx=SoundEffect.FX_VIBRATO`;
+}

From 06826ad958ac0f3b3f0a49070b1f457dc51a3009 Mon Sep 17 00:00:00 2001
From: wilsonkooi <160124853+wilsonkooi@users.noreply.github.com>
Date: Tue, 23 Apr 2024 13:55:14 +0100
Subject: [PATCH 105/106] Fix CodeMirror-Portal integration to handle updates

eq should check all props

updateDOM now re-renders the react component - it's called when eq
returns false. This maintains state and allows props to change.

We take care to cope with the transition to/from rendering the "inline"
case.
---
 src/editor/codemirror/CodeMirror.tsx          | 26 ++++++++--
 .../helper-widgets/reactWidgetExtension.tsx   | 49 +++++++++++++------
 .../helper-widgets/setPixelWidget.tsx         |  4 +-
 .../helper-widgets/widgetArgParser.tsx        |  3 +-
 4 files changed, 58 insertions(+), 24 deletions(-)

diff --git a/src/editor/codemirror/CodeMirror.tsx b/src/editor/codemirror/CodeMirror.tsx
index 7173852e7..15b4c4c5e 100644
--- a/src/editor/codemirror/CodeMirror.tsx
+++ b/src/editor/codemirror/CodeMirror.tsx
@@ -125,9 +125,25 @@ const CodeMirror = ({
 
   const [portals, setPortals] = useState<PortalContent[]>([]);
   const portalFactory: PortalFactory = useCallback((dom, content) => {
-    const portal = { dom, content };
-    setPortals((portals) => [...portals, portal]);
-    return () => setPortals((portals) => portals.filter((p) => p !== portal));
+    setPortals((portals) => {
+      let found = false;
+      let updated = portals.map((p) => {
+        if (p.dom === dom) {
+          found = true;
+          return {
+            dom,
+            content,
+          };
+        }
+        return p;
+      });
+      if (!found) {
+        updated = [...portals, { dom, content }];
+      }
+      return updated;
+    });
+
+    return () => setPortals((portals) => portals.filter((p) => p.dom !== dom));
   }, []);
 
   useEffect(() => {
@@ -143,7 +159,7 @@ const CodeMirror = ({
           logPastedLineCount(logging, update);
         }
       });
-      
+
       const state = EditorState.create({
         doc: defaultValue,
         extensions: [
@@ -331,4 +347,4 @@ const logPastedLineCount = (logging: Logging, update: ViewUpdate) => {
     );
 };
 
-export default CodeMirror;
\ No newline at end of file
+export default CodeMirror;
diff --git a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
index 1b053b52c..2480b27e0 100644
--- a/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
+++ b/src/editor/codemirror/helper-widgets/reactWidgetExtension.tsx
@@ -9,7 +9,7 @@ import { syntaxTree } from "@codemirror/language";
 import { PortalFactory } from "../CodeMirror";
 import React from "react";
 import { createWidget } from "./widgetArgParser";
-import { OpenReactComponent, openWidgetEffect } from "./openWidgets";
+import { openWidgetEffect } from "./openWidgets";
 import { ValidateComponentArgs } from "./widgetArgParser";
 
 export interface WidgetProps {
@@ -43,25 +43,44 @@ class Widget extends WidgetType {
 
   eq(other: WidgetType): boolean {
     const them = other as Widget;
-    return them.component === this.component && them.props.to === this.props.to && them.inline === this.inline;
+    let args1 = this.props.args;
+    let args2 = them.props.args;
+    let eqArgs =
+      args1.length === args2.length &&
+      args1.every((element, index) => element === args2[index]);
+
+    return (
+      them.component === this.component &&
+      them.props.to === this.props.to &&
+      eqArgs &&
+      them.inline === this.inline
+    );
+  }
+
+  updateDOM(dom: HTMLElement, view: EditorView): boolean {
+    dom.style.display = this.inline ? "inline-block" : "unset";
+    this.portalCleanup = this.createPortal(dom, this.toComponent(view));
+    return true;
+  }
+
+  private toComponent(view: EditorView) {
+    if (this.inline) {
+      return <this.open loc={this.props.to} view={view} />;
+    }
+    return <this.component props={this.props} view={view} />;
   }
 
   toDOM(view: EditorView) {
     const dom = document.createElement("div");
 
-    if (this.inline) {
-      if (ValidateComponentArgs(this.component, this.props.args, this.props.types)) {
-        dom.style.display = "inline-block"; // want it inline for the open-close widget
-        this.portalCleanup = this.createPortal(
-          dom,
-          <this.open loc={this.props.to} view={view} />
-        );
-      }
-    } else
-      this.portalCleanup = this.createPortal(
-        dom,
-        <this.component props={this.props} view={view} />
-      );
+    if (
+      this.inline &&
+      !ValidateComponentArgs(this.component, this.props.args, this.props.types)
+    ) {
+      return dom;
+    }
+    dom.style.display = this.inline ? "inline-block" : "unset";
+    this.portalCleanup = this.createPortal(dom, this.toComponent(view));
     return dom;
   }
 
diff --git a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
index 7769e701b..f61a93202 100644
--- a/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/setPixelWidget.tsx
@@ -69,9 +69,9 @@ const MicrobitSinglePixelGrid: React.FC<MicrobitSinglePixelGridProps> = ({
           style={{ marginLeft: "15px", marginTop: "15px", marginBottom: "15px" }}
         >
           {[...Array(5)].map((_, gridY) => (
-            <Box key={y} display="flex">
+            <Box key={gridY} display="flex">
               {[...Array(5)].map((_, gridX) => (
-                <Box key={x} display="flex" mr="0px">
+                <Box key={gridX} display="flex" mr="0px">
                   <Button
                     height="32px"
                     width="30px"
diff --git a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
index db6da1964..176cd8b6e 100644
--- a/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
+++ b/src/editor/codemirror/helper-widgets/widgetArgParser.tsx
@@ -36,7 +36,7 @@ export function createWidget(
       break;
     default:
       // No widget implemented for this function
-      console.log("No widget implemented for this function: " + name);
+      // console.log("No widget implemented for this function: " + name);
       return null;
   }
   if (component) {
@@ -60,7 +60,6 @@ function getChildNodes(node: SyntaxNode): SyntaxNode[] {
   let child = node.firstChild?.nextSibling;
   let children = [];
   while (child && child.name !== ")") {
-    console.log(child.name);
     if (child.name !== "," && child.name !== "Comment") children.push(child);
     child = child.nextSibling;
   }

From 52fa8528b6b5f3fc1db3a31934340f38b850e05a Mon Sep 17 00:00:00 2001
From: Matt Hillsdon <matt.hillsdon@microbit.org>
Date: Tue, 30 Apr 2024 14:44:26 +0100
Subject: [PATCH 106/106] Illustration of stateless version

---
 .../codemirror/helper-widgets/soundWidget.tsx | 444 ++++++++++--------
 1 file changed, 255 insertions(+), 189 deletions(-)

diff --git a/src/editor/codemirror/helper-widgets/soundWidget.tsx b/src/editor/codemirror/helper-widgets/soundWidget.tsx
index 1fea4bdfe..f999f19e3 100644
--- a/src/editor/codemirror/helper-widgets/soundWidget.tsx
+++ b/src/editor/codemirror/helper-widgets/soundWidget.tsx
@@ -1,25 +1,24 @@
-import React, { useState } from "react";
-import {
-  Box,
-  Button,
-  SliderTrack,
-  SliderFilledTrack,
-  SliderThumb,
-  HStack,
-} from "@chakra-ui/react";
+import { Box, Button, HStack } from "@chakra-ui/react";
 import { EditorView } from "@codemirror/view";
-import { WidgetProps } from "./reactWidgetExtension";
+import React, { useState } from "react";
 import { openWidgetEffect } from "./openWidgets";
-import { zIndexAboveDialogs } from "../../../common/zIndex";
-import { start } from "repl";
+import { WidgetProps } from "./reactWidgetExtension";
 
-type FixedLengthArray = [number, number, number, number, number, string, string];
+type FixedLengthArray = [
+  number,
+  number,
+  number,
+  number,
+  number,
+  string,
+  string
+];
 
 interface SliderProps {
   min: number;
   max: number;
   step: number;
-  defaultValue: number;
+  value: number;
   onChange: (value: number) => void;
   sliderStyle?: React.CSSProperties;
   label: string;
@@ -27,14 +26,10 @@ interface SliderProps {
   colour: string;
 }
 
-const startVolProps: SliderProps = {
+const startVolProps: Omit<SliderProps, "value" | "onChange"> = {
   min: 0,
   max: 255,
   step: 1,
-  defaultValue: 50,
-  onChange: (value) => {
-    console.log("Slider value changed:", value);
-  },
   sliderStyle: {
     width: "100%", // Adjust the width of the slider
     height: "100px", // Adjust the height of the slider
@@ -45,17 +40,13 @@ const startVolProps: SliderProps = {
   },
   label: "Start Vol",
   vertical: true,
-  colour: 'red'
+  colour: "red",
 };
 
-const endFrequencySliderProps: SliderProps = {
+const endFrequencySliderProps: Omit<SliderProps, "value" | "onChange"> = {
   min: 0,
   max: 9999,
   step: 1,
-  defaultValue: 5000,
-  onChange: (value) => {
-    console.log("Slider value changed:", value);
-  },
   sliderStyle: {
     width: "100%",
     height: "100px",
@@ -66,17 +57,13 @@ const endFrequencySliderProps: SliderProps = {
   },
   label: "End Freq",
   vertical: true,
-  colour: 'green'
+  colour: "green",
 };
 
-const startFrequencySliderProps: SliderProps = {
+const startFrequencySliderProps: Omit<SliderProps, "value" | "onChange"> = {
   min: 0,
   max: 9999,
   step: 1,
-  defaultValue: 5000,
-  onChange: (value) => {
-    console.log("Slider value changed:", value);
-  },
   sliderStyle: {
     width: "200%", // Adjust the width of the slider
     height: "100px", // Adjust the height of the slider
@@ -87,17 +74,13 @@ const startFrequencySliderProps: SliderProps = {
   },
   label: "Start Freq",
   vertical: true,
-  colour: 'blue'
+  colour: "blue",
 };
 
-const endVolProps: SliderProps = {
+const endVolProps: Omit<SliderProps, "value" | "onChange"> = {
   min: 0,
   max: 255,
   step: 1,
-  defaultValue: 50,
-  onChange: (value) => {
-    console.log("Slider value changed:", value);
-  },
   sliderStyle: {
     width: "200%", // Adjust the width of the slider
     height: "100px", // Adjust the height of the slider
@@ -108,74 +91,88 @@ const endVolProps: SliderProps = {
   },
   label: "End Vol",
   vertical: true,
-  colour: 'black'
+  colour: "black",
 };
 
-const Slider: React.FC<SliderProps & { vertical?: boolean, colour: string }> = ({
-  min,
-  max,
-  step,
-  defaultValue,
-  onChange,
-  sliderStyle,
-  label,
-  vertical,
-  colour
-}) => {
-  const [value, setValue] = useState(defaultValue);
-
-  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-    const newValue = parseFloat(event.target.value);
-    setValue(newValue);
-    onChange(newValue);
-  };
-
-  return (
-    <div style={{ position: 'relative', height: '80px', width: '45px', display: "flex", flexDirection: "column", alignItems: "center" }}>
-      <input
-        type="range"
-        min={min}
-        max={max}
-        step={step}
-        value={value}
-        onChange={handleChange}
-        style={{
-          position: 'absolute',
-          width: '115px', // Width of the slider
-          height: '40px', // Height of the slider
-          transform: 'rotate(-90deg)', // Rotate the slider to vertical orientation
-          accentColor: colour,
-          bottom: '0%',
-        }}
-      />
+const Slider: React.FC<SliderProps & { vertical?: boolean; colour: string }> =
+  ({
+    min,
+    max,
+    step,
+    value,
+    onChange,
+    sliderStyle,
+    label,
+    vertical,
+    colour,
+  }) => {
+    const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+      const newValue = parseFloat(event.target.value);
+      onChange(newValue);
+    };
+
+    return (
       <div
         style={{
-          position: 'absolute',
-          left: 'calc(100% - 15px)', // Position the label to the right of the slider
-          bottom: `${((value - min) / (max - min)) * 100}%`, // Calculate the position based on value
-          transform: 'translateY(50%)', // Center the label vertically with the thumb
-          fontSize: '13px', // Font size of the label
+          position: "relative",
+          height: "80px",
+          width: "45px",
+          display: "flex",
+          flexDirection: "column",
+          alignItems: "center",
         }}
       >
-        {value}
-      </div>
-      <div style={{ marginTop: '120px', textAlign: 'center', fontSize: '11px', }}>
-        <b>{label}</b>
+        <input
+          type="range"
+          min={min}
+          max={max}
+          step={step}
+          value={value}
+          onChange={handleChange}
+          style={{
+            position: "absolute",
+            width: "115px", // Width of the slider
+            height: "40px", // Height of the slider
+            transform: "rotate(-90deg)", // Rotate the slider to vertical orientation
+            accentColor: colour,
+            bottom: "0%",
+          }}
+        />
+        <div
+          style={{
+            position: "absolute",
+            left: "calc(100% - 15px)", // Position the label to the right of the slider
+            bottom: `${((value - min) / (max - min)) * 100}%`, // Calculate the position based on value
+            transform: "translateY(50%)", // Center the label vertically with the thumb
+            fontSize: "13px", // Font size of the label
+          }}
+        >
+          {value}
+        </div>
+        <div
+          style={{ marginTop: "120px", textAlign: "center", fontSize: "11px" }}
+        >
+          <b>{label}</b>
+        </div>
       </div>
-    </div>
-  );
-};
-
+    );
+  };
 
 const TripleSliderWidget: React.FC<{
-  freqStartProps: SliderProps;
-  freqEndProps: SliderProps;
-  volStartProps: SliderProps;
-  volEndprops: SliderProps;
+  freqStartProps: Omit<SliderProps, "value" | "onChange">;
+  freqEndProps: Omit<SliderProps, "value" | "onChange">;
+  volStartProps: Omit<SliderProps, "value" | "onChange">;
+  volEndprops: Omit<SliderProps, "value" | "onChange">;
   props: WidgetProps;
   view: EditorView;
-}> = ({ freqStartProps, freqEndProps, volStartProps, volEndprops, props, view}) => {
-
+}> = ({
+  freqStartProps,
+  freqEndProps,
+  volStartProps,
+  volEndprops,
+  props,
+  view,
+}) => {
   let args = props.args;
   let ranges = props.ranges;
   let types = props.types;
@@ -184,39 +181,37 @@ const TripleSliderWidget: React.FC<{
 
   //parse args
 
-  let argsToBeUsed: FixedLengthArray = [200, 500, 2000, 50, 50, "sine", "None"] // default args
-  let count = 0
-  for (let i = 2; i < args.length; i += 3) { //Update default args with user args where they exist
-    argsToBeUsed[count] = args[i]
+  let argsToBeUsed: FixedLengthArray = [200, 500, 2000, 50, 50, "sine", "None"]; // default args
+  let count = 0;
+  for (let i = 2; i < args.length; i += 3) {
+    //Update default args with user args where they exist
+    argsToBeUsed[count] = args[i];
     let arg = args[i];
     console.log("arg: ", arg);
     count += 1;
-  };
-
-  console.log("args", argsToBeUsed)
-
-  const [initialFrequency, setInitialFrequency] = useState(Math.min(argsToBeUsed[0], 9999));
-  freqStartProps.defaultValue = initialFrequency
-
-  const [endFrequency, setEndFrequency] = useState(Math.min(argsToBeUsed[1], 9999));
-  freqEndProps.defaultValue = endFrequency
+  }
 
+  console.log("args", argsToBeUsed);
 
-  const [startAmplitude, setStartAmplitude] = useState(Math.min(argsToBeUsed[3], 255));
-  volStartProps.defaultValue = startAmplitude
+  const startFreq = Math.min(argsToBeUsed[0], 9999);
+  const endFreq = Math.min(argsToBeUsed[1], 9999);
+  const startVol = Math.min(argsToBeUsed[3], 255);
+  const endVol = Math.min(argsToBeUsed[4], 9999);
 
-  const [endAmplitude, setEndAmplitude] = useState(Math.min(argsToBeUsed[4], 9999));
-  volEndprops.defaultValue = endAmplitude
+  const [waveType, setWaveType] = useState("sine");
 
-  
-  const [waveType, setWaveType] = useState("sine")
+  const waveformOptions = ["None", "Vibrato", "Tremolo", "Warble"];
+  const textBoxValue = Number(argsToBeUsed[2]);
 
-  const waveformOptions = ["None", "Vibrato", "Tremolo", "Warble"]
-  const [textBoxValue, setTextBoxValue] = useState(Number(argsToBeUsed[2]));
-
-
-  const updateView = () => {
-    let insertion = statesToString(initialFrequency, endFrequency, textBoxValue, startAmplitude, endAmplitude);
+  const updateView = (change: Partial<ParsedArgs>) => {
+    let insertion = statesToString({
+      startFreq,
+      endFreq,
+      duration: textBoxValue,
+      startVol,
+      endVol,
+      ...change,
+    });
     console.log(insertion);
     if (ranges.length === 1) {
       view.dispatch({
@@ -241,78 +236,86 @@ const TripleSliderWidget: React.FC<{
     }
   };
 
-
   const handleTextInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-    const newValue = e.target.value;
-    setTextBoxValue(Number(newValue));
-    updateView();
+    //const newValue = e.target.value;
+    //setTextBoxValue(Number(newValue));
+    updateView({});
   };
 
   const handleSlider1Change = (value: number) => {
-    freqStartProps.onChange(value);
-    setInitialFrequency(value);
-    updateView();
+    //freqStartProps.onChange(value);
+    //setInitialFrequency(value);
+    updateView({
+      startFreq: value,
+    });
   };
 
   const handleSlider2Change = (value: number) => {
-    freqEndProps.onChange(value);
-    setEndFrequency(value); // 
-    updateView();
+    //freqEndProps.onChange(value);
+    //setEndFrequency(value); //
+    updateView({});
   };
 
   const handleSlider3Change = (value: number) => {
-    freqStartProps.onChange(value);
-    setStartAmplitude(value);
-    updateView();
+    //freqStartProps.onChange(value);
+    //setStartAmplitude(value);
+    updateView({});
   };
 
   const handleSlider4Change = (value: number) => {
-    freqStartProps.onChange(value);
-    setEndAmplitude(value);
-    updateView();
+    //freqStartProps.onChange(value);
+    //setEndAmplitude(value);
+    updateView({});
   };
 
   const handleWaveTypeChange = (value: string) => {
     setWaveType(value);
   };
 
-
-
   const generateWavePath = () => {
     const waveLength = 400; // Width of the box
     const pathData = [];
 
-    const frequencyDifference = endFrequency - initialFrequency;
-    const amplitudeDifference = endAmplitude - startAmplitude;
+    const frequencyDifference = endFreq - startFreq;
+    const amplitudeDifference = endVol - startVol;
 
     // Loop through the wave's width to generate the path
     for (let x = 0; x <= waveLength; x++) {
-      const currentFrequency = (initialFrequency + (frequencyDifference * x) / waveLength)/100;
-      const currentAmplitude = (startAmplitude + (amplitudeDifference * x) / waveLength)/2.2;
-      const period = waveLength / currentFrequency
-
+      const currentFrequency =
+        (startFreq + (frequencyDifference * x) / waveLength) / 100;
+      const currentAmplitude =
+        (startVol + (amplitudeDifference * x) / waveLength) / 2.2;
+      const period = waveLength / currentFrequency;
 
       // Calculate the y-coordinate based on the current frequency and amplitude
       let y = 0;
       switch (waveType) {
-        case 'sine':
+        case "sine":
           y = 65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
           break;
-        case 'square':
-          y = x % period < period / 2 ? 65 + currentAmplitude : 65 - currentAmplitude;
+        case "square":
+          y =
+            x % period < period / 2
+              ? 65 + currentAmplitude
+              : 65 - currentAmplitude;
           break;
-        case 'sawtooth':
-          y = 65 + currentAmplitude - ((x % period) / period) * (2 * currentAmplitude);
+        case "sawtooth":
+          y =
+            65 +
+            currentAmplitude -
+            ((x % period) / period) * (2 * currentAmplitude);
           break;
-        case 'triangle':
+        case "triangle":
           const tPeriod = x % period;
-          y = tPeriod < period / 2
-            ? 65 + (2 * currentAmplitude / period) * tPeriod
-            : 65 - (2 * currentAmplitude / period) * (tPeriod - period / 2);
+          y =
+            tPeriod < period / 2
+              ? 65 + ((2 * currentAmplitude) / period) * tPeriod
+              : 65 - ((2 * currentAmplitude) / period) * (tPeriod - period / 2);
           break;
-        case 'noisy':
+        case "noisy":
           // Generate noisy wave based on sine wave and random noise
-          const baseWave = 65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
+          const baseWave =
+            65 + currentAmplitude * Math.sin((x / period) * 2 * Math.PI);
           const randomNoise = Math.random() * 2 - 1;
           y = baseWave + randomNoise * (currentAmplitude * 0.3);
           break;
@@ -323,34 +326,74 @@ const TripleSliderWidget: React.FC<{
     }
 
     // Join the path data points to create the path
-    return `M${pathData.join(' ')}`;
+    return `M${pathData.join(" ")}`;
   };
 
   return (
     <div>
-      <div style={{ display: "flex", justifyContent: "flex-start", backgroundColor: 'snow', width: '575px', height: '150px', border: '1px solid lightgray', boxShadow: '0 0 10px 5px rgba(173, 216, 230, 0.7)', zIndex: 10 }}>
+      <div
+        style={{
+          display: "flex",
+          justifyContent: "flex-start",
+          backgroundColor: "snow",
+          width: "575px",
+          height: "150px",
+          border: "1px solid lightgray",
+          boxShadow: "0 0 10px 5px rgba(173, 216, 230, 0.7)",
+          zIndex: 10,
+        }}
+      >
         {/* Vertical Slider 1 */}
-        <div style={{ marginLeft: "6px", marginRight: "20px", height: '100px', marginTop: '9px' }}>
-          <Slider {...freqStartProps} onChange={handleSlider1Change} vertical />
+        <div
+          style={{
+            marginLeft: "6px",
+            marginRight: "20px",
+            height: "100px",
+            marginTop: "9px",
+          }}
+        >
+          <Slider
+            {...freqStartProps}
+            value={startFreq}
+            onChange={handleSlider1Change}
+            vertical
+          />
         </div>
         {/* Vertical Slider 2 */}
-        <div style={{ marginRight: "20px", height: '100px', marginTop: '9px' }}>
-          <Slider {...freqEndProps} onChange={handleSlider2Change} vertical />
+        <div style={{ marginRight: "20px", height: "100px", marginTop: "9px" }}>
+          <Slider
+            {...freqEndProps}
+            // TODO: for this and all the following sliders we need value to come from the parsed args above
+            //       and the handleXXXChange functions need to be updated to pass the relevant change to updateView
+            value={0}
+            onChange={handleSlider2Change}
+            vertical
+          />
         </div>
         {/* Vertical Slider 3 */}
-        <div style={{ marginRight: "20px", height: '100px', marginTop: '9px' }}>
-          <Slider {...volStartProps} onChange={handleSlider3Change} vertical />
+        <div style={{ marginRight: "20px", height: "100px", marginTop: "9px" }}>
+          <Slider
+            {...volStartProps}
+            value={0}
+            onChange={handleSlider3Change}
+            vertical
+          />
         </div>
         {/* Vertical Slider 4 */}
-        <div style={{ marginRight: "25px", height: '100px', marginTop: '9px' }}>
-          <Slider {...volEndprops} onChange={handleSlider4Change} vertical />
+        <div style={{ marginRight: "25px", height: "100px", marginTop: "9px" }}>
+          <Slider
+            {...volEndprops}
+            value={0}
+            onChange={handleSlider4Change}
+            vertical
+          />
         </div>
 
-
-        <div style={{ marginRight: '10px', height: '100px', fontSize: '12px' }}>
-
+        <div style={{ marginRight: "10px", height: "100px", fontSize: "12px" }}>
           {/* waveform type selection */}
-          <label style={{ display: 'block', marginBottom: '5px', marginTop: '7px', }}>
+          <label
+            style={{ display: "block", marginBottom: "5px", marginTop: "7px" }}
+          >
             <b>Waveform:</b>
           </label>
           <select onChange={(e) => handleWaveTypeChange(e.target.value)}>
@@ -363,7 +406,9 @@ const TripleSliderWidget: React.FC<{
 
           {/* fx type selection */}
 
-          <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px' }}>
+          <label
+            style={{ display: "block", marginBottom: "5px", marginTop: "10px" }}
+          >
             <b>Effects:</b>
           </label>
           <select onChange={(e) => handleWaveTypeChange(e.target.value)}>
@@ -375,7 +420,9 @@ const TripleSliderWidget: React.FC<{
 
           {/* Duration selctor */}
 
-          <label style={{ display: 'block', marginBottom: '5px', marginTop: '10px' }}>
+          <label
+            style={{ display: "block", marginBottom: "5px", marginTop: "10px" }}
+          >
             <b>Duration(ms):</b>
           </label>
           {/* Input field with associated datalist */}
@@ -384,12 +431,19 @@ const TripleSliderWidget: React.FC<{
             value={textBoxValue}
             onChange={handleTextInputChange} // Handle the selected or typed-in value
             defaultValue="2000"
-            style={{ width: '75px' }}
+            style={{ width: "75px" }}
           />
-
         </div>
         {/* Waveform box */}
-        <div style={{ width: '200px', height: '130px', backgroundColor: 'linen', marginTop: '9px', marginLeft: '5px' }}>
+        <div
+          style={{
+            width: "200px",
+            height: "130px",
+            backgroundColor: "linen",
+            marginTop: "9px",
+            marginLeft: "5px",
+          }}
+        >
           <svg width="100%" height="100%">
             <path d={generateWavePath()} stroke="black" fill="none" />
             <line
@@ -404,12 +458,9 @@ const TripleSliderWidget: React.FC<{
         </div>
       </div>
     </div>
-
   );
 };
 
-
-
 export const SoundComponent = ({
   props,
   view,
@@ -417,12 +468,11 @@ export const SoundComponent = ({
   props: WidgetProps;
   view: EditorView;
 }) => {
-
   let args = props.args;
   let ranges = props.ranges;
   let types = props.types;
   let from = props.from;
-  let to = props.to
+  let to = props.to;
 
   //for future reference add a aclose button
   const handleCloseClick = () => {
@@ -430,7 +480,7 @@ export const SoundComponent = ({
       effects: [openWidgetEffect.of(-1)],
     });
   };
-  
+
   const updateView = () => {
     let insertion = "test";
     console.log(insertion);
@@ -456,7 +506,7 @@ export const SoundComponent = ({
       });
     }
   };
-  
+
   return (
     <HStack fontFamily="body" spacing={5} py={3} zIndex={10}>
       <Box ml="10px" style={{ marginRight: "4px" }}>
@@ -478,13 +528,29 @@ export const SoundComponent = ({
 
 //(startFreq: number, endFreq: Number, duration: Number, startVol: number, endVol: Number, waveform: string, fx: string)
 
-function statesToString(startFreq: number, endFreq: Number, duration: Number, startVol: number, endVol: Number): string {
-  return `\n`
-  + `        freq_start=${startFreq},\n`
-  + `        freq_end=${endFreq},\n`
-  + `        duration=${duration},\n`
-  + `        vol_start=${startVol},\n`
-  + `        vol_end=${endVol},\n`
-  + `        waveform=SoundEffect.FX_WARBLE,\n`
-  + `        fx=SoundEffect.FX_VIBRATO`;
+interface ParsedArgs {
+  startFreq: number;
+  endFreq: number;
+  duration: number;
+  startVol: number;
+  endVol: number;
+}
+
+function statesToString({
+  startFreq,
+  endFreq,
+  duration,
+  startVol,
+  endVol,
+}: ParsedArgs): string {
+  return (
+    `\n` +
+    `        freq_start=${startFreq},\n` +
+    `        freq_end=${endFreq},\n` +
+    `        duration=${duration},\n` +
+    `        vol_start=${startVol},\n` +
+    `        vol_end=${endVol},\n` +
+    `        waveform=SoundEffect.FX_WARBLE,\n` +
+    `        fx=SoundEffect.FX_VIBRATO`
+  );
 }