diff --git a/src/components/Blockly/BlocklyComponent.jsx b/src/components/Blockly/BlocklyComponent.jsx index 5412bddb..03b0e268 100644 --- a/src/components/Blockly/BlocklyComponent.jsx +++ b/src/components/Blockly/BlocklyComponent.jsx @@ -108,7 +108,11 @@ class BlocklyComponent extends React.Component { + <> ) : null} - + ); } } diff --git a/src/components/BoardSelector.jsx b/src/components/BoardSelector.jsx new file mode 100644 index 00000000..b5462c3a --- /dev/null +++ b/src/components/BoardSelector.jsx @@ -0,0 +1,90 @@ +import React from "react"; +import { Button, Menu, MenuItem } from "@mui/material"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faMicrochip, faCaretDown } from "@fortawesome/free-solid-svg-icons"; + +/** + * BoardSelector component for selecting MCU board type. + * Props: + * - selectedBoard: string ("mcu", "mini", or "esp32") + * - setBoard: function to set the selected board + */ +class BoardSelector extends React.Component { + constructor(props) { + super(props); + this.mcuRef = React.createRef(); + this.state = { + anchorElBoard: null, + }; + } + + handleOpenMenu = () => { + this.setState({ anchorElBoard: this.mcuRef.current }); + }; + + handleCloseMenu = () => { + this.setState({ anchorElBoard: null }); + }; + + handleSelectBoard = (event) => { + this.props.setBoard(event.currentTarget.getAttribute("value")); + this.handleCloseMenu(); + }; + + render() { + const { selectedBoard } = this.props; + return ( +
+ + + + MCU + + + MCU:mini + + + MCU-S2 + + +
+ ); + } +} + +export default BoardSelector; diff --git a/src/components/Content.jsx b/src/components/Content.jsx index 9d51d27e..9fb37ea6 100644 --- a/src/components/Content.jsx +++ b/src/components/Content.jsx @@ -1,6 +1,7 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; +import { withRouter } from "react-router-dom"; import * as Blockly from "blockly/core"; import { De } from "./Blockly/msg/de"; @@ -34,6 +35,13 @@ class Content extends Component { } render() { + const { location } = this.props; + if ( + (location && location.pathname === "/minimal") || + location.pathname === "/docs" + ) { + return ; + } return (
@@ -47,6 +55,7 @@ class Content extends Component { Content.propTypes = { language: PropTypes.string.isRequired, + minimal: PropTypes.bool, }; const mapStateToProps = (state) => ({ @@ -54,4 +63,4 @@ const mapStateToProps = (state) => ({ board: state.board.board, }); -export default connect(mapStateToProps, null)(Content); +export default connect(mapStateToProps, null)(withRouter(Content)); diff --git a/src/components/DocsHome.jsx b/src/components/DocsHome.jsx new file mode 100644 index 00000000..488d480d --- /dev/null +++ b/src/components/DocsHome.jsx @@ -0,0 +1,48 @@ +import React, { Component } from "react"; +import * as Blockly from "blockly/core"; + +import BlocklyWindow from "./Blockly/BlocklyWindow"; + +class DocsHome extends Component { + constructor(props) { + super(props); + this.state = { + xml: null, // Start with no XML + }; + } + + componentDidMount() { + window.addEventListener("message", this.handlePostMessage); + console.log("DocsHome mounted: waiting for postMessage..."); + } + + componentWillUnmount() { + window.removeEventListener("message", this.handlePostMessage); + } + + handlePostMessage = (event) => { + try { + console.log("Received postMessage:", event); + const data = + typeof event.data === "string" ? JSON.parse(event.data) : event.data; + + if (data && data.type === "load-xml" && data.xml) { + console.log("Received XML via postMessage:", data.xml); + + this.setState({ xml: data.xml }); + } + } catch (e) { + console.error("Invalid XML in postMessage:", e); + } + }; + + render() { + return ( +
+ +
+ ); + } +} + +export default DocsHome; diff --git a/src/components/MinimalHome.jsx b/src/components/MinimalHome.jsx new file mode 100644 index 00000000..f2b79913 --- /dev/null +++ b/src/components/MinimalHome.jsx @@ -0,0 +1,159 @@ +import { Component } from "react"; +import PropTypes from "prop-types"; +import { connect } from "react-redux"; +import { clearStats, workspaceName } from "../actions/workspaceActions"; +import * as Blockly from "blockly/core"; +import BlocklyWindow from "./Blockly/BlocklyWindow"; +import store from "../store"; +import { setPlatform, setRenderer } from "../actions/generalActions"; +import BoardSelector from "./BoardSelector"; +import { setBoard } from "../actions/boardAction"; + +class MinimalHome extends Component { + constructor(props) { + super(props); + this.state = { + initialXml: localStorage.getItem("autoSaveXML"), + }; + } + + componentDidMount() { + store.dispatch(setRenderer("zelos")); + window.localStorage.setItem("ota", true); + store.dispatch(setPlatform(false)); + // Listen for messages from Flutter + window.addEventListener("message", this.handleFlutterMessage); + } + + componentDidUpdate(props) { + /* Resize and reposition all of the workspace chrome (toolbox, trash, + scrollbars etc.) This should be called when something changes that requires + recalculating dimensions and positions of the trash, zoom, toolbox, etc. + (e.g. window resize). */ + const workspace = Blockly.getMainWorkspace(); + Blockly.svgResize(workspace); + } + + componentWillUnmount() { + this.props.clearStats(); + this.props.workspaceName(null); + window.localStorage.removeItem("ota"); + window.removeEventListener("message", this.handleFlutterMessage); + } + + handleFlutterMessage = (event) => { + // You may want to check event.origin for security in production + try { + const data = + typeof event.data === "string" ? JSON.parse(event.data) : event.data; + if (data && data.action === "triggerCompile") { + this.handleCompileFromFlutter(); + } + } catch (e) { + // Ignore invalid JSON + } + }; + + handleCompileFromFlutter = async () => { + try { + // Simulate some work (replace with your actual logic) + const board = + this.props.selectedBoard === "mcu" || + this.props.selectedBoard === "mini" + ? "sensebox-mcu" + : "sensebox-esp32s2"; + + const response = await fetch(`${this.props.compilerUrl}/compile`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + sketch: this.props.arduinoCode, + board, + }), + }); + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || "Compilation failed"); + } + if (data.data.id) { + const result = { + status: "success", + message: "Compile finished", + sketchId: data.data.id, + board: board, + }; + window.FlutterChannel.postMessage(JSON.stringify(result)); + return; + } + } catch (e) { + const result = { + status: "error", + message: e.message || "An error occurred", + }; + window.FlutterChannel.postMessage(JSON.stringify(result)); + return; + } + }; + + onChange = () => { + this.setState({ codeOn: !this.state.codeOn }); + const workspace = Blockly.getMainWorkspace(); + // https://github.com/google/blockly/blob/master/core/blockly.js#L314 + if (workspace.trashcan && workspace.trashcan.flyout) { + workspace.trashcan.flyout.hide(); // in case of resize, the trash flyout does not reposition + } + }; + + render() { + return ( +
+
+ +
+
+ +
+
+ ); + } +} + +MinimalHome.propTypes = { + platform: PropTypes.bool.isRequired, + arduinoCode: PropTypes.string.isRequired, + board: PropTypes.string.isRequired, + compilerUrl: PropTypes.string.isRequired, + setBoard: PropTypes.func.isRequired, +}; + +const mapStateToProps = (state) => ({ + platform: state.general.platform, + arduinoCode: state.workspace.code.arduino, + selectedBoard: state.board.board, + compilerUrl: state.general.compiler, +}); + +export default connect(mapStateToProps, { + clearStats, + workspaceName, + setBoard, +})(MinimalHome); diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 886fee10..5df570ea 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -21,7 +21,7 @@ import ListItemIcon from "@mui/material/ListItemIcon"; import ListItemText from "@mui/material/ListItemText"; import LinearProgress from "@mui/material/LinearProgress"; import Tour from "reactour"; -import { Badge } from "@mui/material"; +import { Badge, Box, Container } from "@mui/material"; import { home, assessment } from "./Tour"; import { faBars, @@ -51,6 +51,7 @@ import Menu from "@mui/material/Menu"; import { setLanguage } from "../actions/generalActions"; import { setBoard } from "../actions/boardAction"; import { Button } from "@mui/material"; +import BoardSelector from "./BoardSelector"; const styles = (theme) => ({ drawerWidth: { @@ -167,100 +168,20 @@ class Navbar extends Component { > {isHome ? (
-
- - { - this.setState({ - anchorElBoard: null, - }); - }} - > - { - this.props.setBoard( - event.currentTarget.getAttribute("value"), - ); - this.setState({ - anchorElBoard: null, - }); - }} - > - MCU - - { - this.props.setBoard( - event.currentTarget.getAttribute("value"), - ); - this.setState({ - anchorElBoard: null, - }); - }} - > - MCU:mini - - { - this.props.setBoard( - event.currentTarget.getAttribute("value"), - ); - this.setState({ - anchorElBoard: null, - }); - }} - > - MCU-S2 - - -
+ + +
{this.props.language === "en_US" ? (
+ //
+ + + + + + + + + + + {/* Tutorials */} + + + + + + + + + + + + + {/* Sharing */} + + + + {/* Gallery-Projects */} + + + + + + + {/* User-Projects */} + + + + + + + {/* User */} + + + + + + + {/* settings */} + + + + {/* privacy */} + + + + + + + + + + + + + {/* Not Found */} + + + + + //
); } } diff --git a/src/index.css b/src/index.css index 4a1df4db..c40cb596 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,19 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", + "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + font-family: + source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; +} + +html, +body, +#root { + height: 100%; }