Skip to content
Draft
4 changes: 2 additions & 2 deletions cli/buildengine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const buildEngines: Map<BuildEngine> = {
codal: {
id: "codal",
updateEngineAsync: updateCodalBuildAsync,
buildAsync: () => runBuildCmdAsync("python", "build.py"),
buildAsync: () => runBuildCmdAsync("python3", "build.py"),
setPlatformAsync: noopAsync,
patchHexInfo: patchCodalHexInfo,
prepBuildDirAsync: prepCodalBuildDirAsync,
Expand All @@ -99,7 +99,7 @@ export const buildEngines: Map<BuildEngine> = {
dockercodal: {
id: "dockercodal",
updateEngineAsync: updateCodalBuildAsync,
buildAsync: () => runDockerAsync(["python", "build.py"]),
buildAsync: () => runDockerAsync(["python3", "build.py"]),
setPlatformAsync: noopAsync,
patchHexInfo: patchCodalHexInfo,
prepBuildDirAsync: prepCodalBuildDirAsync,
Expand Down
1 change: 1 addition & 0 deletions localtypings/pxtarget.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ declare namespace pxt {
forceEnableAiErrorHelp?: boolean; // Enables the AI Error Help feature, regardless of geo setting.
shareHomepageContent?: boolean; // Show buttons to share links to homepage content more easily
showProjectDescription?: boolean; // Show project description in pxtjson editor and share dialog
showJacdac?: boolean; // show jacdac button in the sidebar
}

interface DownloadDialogTheme {
Expand Down
2 changes: 2 additions & 0 deletions localtypings/pxteditor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,7 @@ declare namespace pxt.editor {
feedback?: FeedbackState;
themePickerOpen?: boolean;
errorListNote?: string;
showJacdac?: boolean;
}

export interface EditorState {
Expand Down Expand Up @@ -998,6 +999,7 @@ declare namespace pxt.editor {
stopSimulator(unload?: boolean, opts?: SimulatorStartOptions): void;
restartSimulator(): void;
startSimulator(opts?: SimulatorStartOptions): void;
reinitializeSimulatorAsync(): Promise<void>;
runSimulator(): void;
isSimulatorRunning(): boolean;
expandSimulator(): void;
Expand Down
7 changes: 7 additions & 0 deletions pxteditor/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ export function all(): Experiment[] {
feedbackUrl: "https://github.com/microsoft/pxt/issues/10694",
enableOnline: true
},
{
id: "jacdacUI",
name: lf("New Jacdac UI"),
description: lf("Simplified Jacdac UI"),
// feedbackUrl: "https://github.com/microsoft/pxt/issues/10694",
enableOnline: true
},
];

return exps.filter(experiment => ids.indexOf(experiment.id) > -1)
Expand Down
1 change: 1 addition & 0 deletions pxtlib/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ namespace pxt {
export const CONFIG_NAME = "pxt.json"
export const SIMSTATE_JSON = ".simstate.json"
export const SERIAL_EDITOR_FILE = "serial.txt"
export const JACDAC_EDITOR_FILE = "jacdac.txt"
export const README_FILE = "README.md"
export const GITIGNORE_FILE = ".gitignore"
export const ASSETS_FILE = "assets.json"
Expand Down
109 changes: 107 additions & 2 deletions pxtsim/simdriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,60 @@ namespace pxsim {
this.singleSimulator = true
}

private mode: "simulator" | "devices" = "simulator"
private removedElements: HTMLElement[] = [];
private removedIndices: number[] = [];
private jacdacIndex: number = -1;
public setMode(mode: "simulator" | "devices") {
this.mode = mode
if (this.jacdacIndex != -1 && mode === "simulator") {
// add frames back to the DOM
const jacdacFrame = this.simFrames()[0];
this.removedElements.forEach((elem, index) => {
if (index < this.jacdacIndex) {
this.container.insertBefore(elem, jacdacFrame.parentElement);
} else {
this.container.appendChild(elem);
}
});
this.removedElements = [];
this.removedIndices = [];
this.jacdacIndex = -1;
this.postMessageCore(jacdacFrame, {
type: "simulatorMode",
source: MESSAGE_SOURCE,
});
this.start();
} else if (this.jacdacIndex === -1 && mode === "devices") {
this.suspend();
const frames = this.simFrames();
frames.forEach((frame,index) => {
if (frame.dataset[FRAME_DATA_MESSAGE_CHANNEL] !== "jacdac/pxt-jacdac") {
this.removedElements.push(frame.parentElement);
this.removedIndices.push(index);
} else {
this.jacdacIndex = index;
this.postMessageCore(frame, {
type: "devicesMode",
source: MESSAGE_SOURCE,
});
}
});
this.removedElements.forEach(elem => this.container.removeChild(elem));
}
}

public postMessage(msg: pxsim.SimulatorMessage, source?: Window, frameID?: string) {
if (this.hwdbg) {
this.hwdbg.postMessage(msg)
return
}
if (this.mode === "simulator" && (msg as any)?.sender === "packetio") {
const messageChannel = msg.type === "messagepacket" && (msg as SimulatorControlMessage).channel;
if (messageChannel === "jacdac") {
return; // don't send packetio jacdac messages to sims when in simulator mode
}
}

const depEditors = this.dependentEditors();
let frames = this.simFrames();
Expand All @@ -370,7 +419,8 @@ namespace pxsim {
broadcastmsg.srcFrameIndex = this.simFrames().findIndex((item) => item.contentWindow === source);
const sourceFrame = broadcastmsg.srcFrameIndex >= 0 ? this.simFrames()[broadcastmsg.srcFrameIndex] : undefined;
// jacdac messages from a board sim other than first should be dropped
if (broadcastmsg.srcFrameIndex > 0 && mkcdFrames.find(f => f === sourceFrame) && messageChannel === "jacdac")
if (broadcastmsg.srcFrameIndex > 0 && mkcdFrames.find(f => f === sourceFrame)
&& messageChannel === "jacdac")
return;
// if the editor is hosted in a multi-editor setting
// don't start extra frames
Expand Down Expand Up @@ -477,7 +527,8 @@ namespace pxsim {
if (source && frame.contentWindow == source) continue;
// if jacdac message, don't send to other (board) simulator frames
if (i > 0 && !frame.dataset[FRAME_DATA_MESSAGE_CHANNEL] &&
msg.type === "messagepacket" && (msg as pxsim.SimulatorControlMessage).channel === "jacdac") continue;
msg.type === "messagepacket" &&
(msg as pxsim.SimulatorControlMessage).channel === "jacdac") continue;
// frame not in DOM
if (!frame.contentWindow) continue;

Expand Down Expand Up @@ -667,6 +718,60 @@ namespace pxsim {
&& this.loanedSimulator.querySelector("iframe");
}

// the jacdac simulator frame, visually relocated on top of another element
private jacdacOverlayWrapper: HTMLElement;
private jacdacOverlayTarget: HTMLElement;
private jacdacOverlayResizeObserver: ResizeObserver;
private jacdacOverlayReposition = () => {
if (!this.jacdacOverlayWrapper || !this.jacdacOverlayTarget) return;
const rect = this.jacdacOverlayTarget.getBoundingClientRect();
const style = this.jacdacOverlayWrapper.style;
style.position = "fixed";
style.top = `${rect.top}px`;
style.left = `${rect.left}px`;
style.width = `${rect.width}px`;
style.height = `${rect.height}px`;
style.zIndex = "1000";
}

// visually relocates the jacdac simulator frame on top of `target`, without
// reparenting its iframe: reparenting an iframe forces the browser to tear down
// and reload its nested browsing context, which wipes out all the running
// jacdac module simulators inside it
public showJacdacSimulator(target: HTMLElement): boolean {
this.hideJacdacSimulator();

const wrapper = pxsim.util.toArray(this.container.children)
.find(el => (el.querySelector("iframe") as HTMLIFrameElement)?.dataset[FRAME_DATA_MESSAGE_CHANNEL] === "jacdac/pxt-jacdac") as HTMLElement;
if (!wrapper) return false;

this.jacdacOverlayWrapper = wrapper;
this.jacdacOverlayTarget = target;
this.jacdacOverlayReposition();

if (typeof ResizeObserver !== "undefined") {
this.jacdacOverlayResizeObserver = new ResizeObserver(this.jacdacOverlayReposition);
this.jacdacOverlayResizeObserver.observe(target);
}
window.addEventListener("resize", this.jacdacOverlayReposition);
window.addEventListener("scroll", this.jacdacOverlayReposition, true);
return true;
}

public hideJacdacSimulator() {
if (!this.jacdacOverlayWrapper) return;

const style = this.jacdacOverlayWrapper.style;
style.position = style.top = style.left = style.width = style.height = style.zIndex = "";

window.removeEventListener("resize", this.jacdacOverlayReposition);
window.removeEventListener("scroll", this.jacdacOverlayReposition, true);
this.jacdacOverlayResizeObserver?.disconnect();
this.jacdacOverlayResizeObserver = undefined;
this.jacdacOverlayWrapper = undefined;
this.jacdacOverlayTarget = undefined;
}

private frameCleanupTimeout: any = undefined;
private cancelFrameCleanup() {
if (this.frameCleanupTimeout) {
Expand Down
4 changes: 3 additions & 1 deletion theme/common.less
Original file line number Diff line number Diff line change
Expand Up @@ -1445,7 +1445,8 @@ Field editors
}
}

#serialPreview .label:focus {
#serialPreview .label:focus,
#jacdacPreview .label:focus {
outline: 3px solid var(--pxt-focus-border) !important;
outline-offset: -15px;
}
Expand Down Expand Up @@ -1642,6 +1643,7 @@ p.ui.font.small {
#maineditor,
#editortools,
#serialPreview,
#jacdacPreview,
.settings-menuitem,
.help-dropdown-menuitem {
display: none !important;
Expand Down
6 changes: 4 additions & 2 deletions theme/highcontrast.less
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,8 @@

/* Serial editor */

#serialPreview div {
#serialPreview div,
#jacdacPreview div {
color: @HCtextColor;
}

Expand All @@ -640,7 +641,8 @@
border-color: @HCtextColor;
}

#serialPreview .label {
#serialPreview .label,
#jacdacPreview .label {
border: 10px solid @HCtextColor !important;
&:hover {
border-color: darken(@HCtextColor, 10.0) !important;
Expand Down
29 changes: 29 additions & 0 deletions theme/jacdac.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* Import all components */
@import 'themes/default/globals/site.variables';
@import 'themes/pxt/globals/site.variables';

/* Reference import */
@import (reference) "semantic.less";

/*-------------------
Jacdac editor
--------------------*/

#jacdacEditor {
background-color: var(--pxt-target-background2);
color: var(--pxt-target-foreground2);
}

#jacdacArea {
height: 90%;
padding: 1rem;
display: flex;
flex-direction: column;
}

#jacdacSimulator {
// placeholder box: the actual jacdac sim iframe is positioned on top of
// this element (see SimulatorDriver.showJacdacSimulator), it is not a child of it
flex: 1;
min-height: 20rem;
}
1 change: 1 addition & 0 deletions theme/pxt.less
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@import 'sidedoc-keyboard-nav-help';
@import 'home';
@import 'serial';
@import 'jacdac';
@import 'docs';
@import 'debugger';
@import 'toolbox';
Expand Down
16 changes: 11 additions & 5 deletions theme/serial.less
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,13 @@
margin: 0 !important;
}

#serialPreview {
#serialPreview,
#jacdacPreview {
cursor: pointer;
}

#serialPreview .label {
#serialPreview .label,
#jacdacPreview .label {
width: 100%;
background-color: var(--pxt-target-background2);
font-size: 0.85em;
Expand All @@ -245,16 +247,20 @@
}
}

#serialPreview .label:hover {
#serialPreview .label:hover,
#jacdacPreview .label:hover {
opacity: 0.8;
}

#serialPreview .label:focus {
#serialPreview .label:focus,
#jacdacPreview .label:focus {
outline: none;
}

.fullscreensim #serialPreview,
.simView #serialPreview {
.fullscreensim #jacdacPreview,
.simView #serialPreview,
.simView #jacdacPreview {
display: none !important;
z-index: -10 !important;
}
Expand Down
45 changes: 45 additions & 0 deletions theme/tutorial-sidebar.less
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,51 @@
&.ui.items { margin-top: 0; }
}

.jacdac-view-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0.75rem 0.75rem 0.25rem 0.75rem;
}

.jacdac-view-toggle {
display: inline-flex;
align-self: flex-start;
gap: 0.35rem;
margin: 0;
padding: 0.35rem;
border: 1px solid var(--pxt-target-foreground0);
border-radius: 0.75rem;
background: var(--pxt-target-background1);

> .ui.button {
border: 1px solid var(--pxt-target-foreground0) !important;
border-radius: 0.5rem !important;
box-shadow: none !important;
margin: 0 !important;
color: var(--pxt-button-secondary-foreground) !important;

&:not(.active) {
background: transparent !important;
}

&.active {
background: var(--pxt-colors-blue-background) !important;
border-color: var(--pxt-colors-blue-background) !important;
color: var(--pxt-colors-blue-foreground) !important;
box-shadow: 0 0 0 1px var(--pxt-colors-blue-background), 0 0.15rem 0.35rem rgba(0, 0, 0, 0.18) !important;
}
}
}

.jacdac-view-expand {
border: 1px solid var(--pxt-target-foreground0) !important;
border-radius: 0.5rem !important;
box-shadow: none !important;
background: var(--pxt-target-background1) !important;
color: var(--pxt-button-secondary-foreground) !important;
}

// Mini sim is visible when tab is hidden
#root.tabTutorial {
&:not(.fullscreensim) .simulator-container.hidden {
Expand Down
Loading
Loading