Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@
decoding and drawing GIF frames onto the turtle's overlay canvas.
Canvas refreshing is handled separately by CreateJS Ticker. -->
<script src="js/gif-animator.js" defer></script>
<!-- FirstProjectTutorial.js provides interactive step-by-step onboarding
tutorial that guides new users through creating their first project. -->
<script src="js/tutorial/FirstProjectTutorial.js" defer></script>
<script defer>
document.addEventListener("DOMContentLoaded", function () {
if (window.hljs) hljs.highlightAll();
Expand Down Expand Up @@ -924,6 +927,9 @@
<ul id="helpdropdown" class="dropdown-content helpdropdown-menu">
<li><a id="helpGuideItem" role="button" tabindex="0"></a></li>
<li><a id="shortcutsGuideItem" role="button" tabindex="0"></a></li>
<li>
<a id="interactiveTutorialGuideItem" role="button" tabindex="0"></a>
</li>
</ul>

<ul id="languagedropdown" class="dropdown-content">
Expand Down
42 changes: 40 additions & 2 deletions js/activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -4904,7 +4904,19 @@ class Activity {
}
activity._showKeyboardShortcuts();
};

const showInteractiveTutorial = activity => {
if (window.widgetWindows?.isOpen("help")) {
window.widgetWindows.clear("help");
}
if (window.widgetWindows?.isOpen("keyboard-shortcuts")) {
window.widgetWindows.clear("keyboard-shortcuts");
}
if (typeof FirstProjectTutorial !== "undefined") {
new FirstProjectTutorial(activity).start();
} else {
console.error("FirstProjectTutorial is not loaded");
}
};
this._showKeyboardShortcuts = () => {
const platformKeys = (windowsKeys, macKeys = windowsKeys) =>
`${_("Windows/Linux")}: ${windowsKeys}\n${_("Mac")}: ${macKeys}`;
Expand Down Expand Up @@ -5149,6 +5161,13 @@ class Activity {
widgetWindow.sendToCenter();
requestAnimationFrame(() => widgetWindow.sendToCenter());
};
/**
* Open the help widget at the First Project Tutorial card.
* This can be called from anywhere to launch the tutorial info page.
*/
this.openFirstProjectTutorial = () => {
HelpWidget.openFirstProjectTutorial(this);
};

/*
* Shows about page
Expand Down Expand Up @@ -5718,6 +5737,14 @@ class Activity {
this.addEventListener(document, "mousemove", this.handleMouseMove);
this.addEventListener(document, "click", this.handleDocumentClick);
this.addEventListener(window, "beforeunload", () => {
// Do not save while the interactive tutorial is active —
// the canvas holds a bare-canvas tutorial state, not the
// user's real project. Saving it would corrupt localStorage.
if (this._tutorialActive) {
this._stopRenderLoop();
return;
}

// Save synchronously to SESSION* keys so manual reload/F5
// still has recoverable data even if async saves are cut short.
if (typeof this.__saveLocally === "function") {
Expand Down Expand Up @@ -5826,7 +5853,7 @@ class Activity {
);
this.toolbar.renderPlanetIcon(this.planet, doOpenSamples);
this.toolbar.renderMenuIcon(showHideAuxMenu);
this.toolbar.renderHelpIcon(showHelp, showKeyboardShortcuts);
this.toolbar.renderHelpIcon(showHelp, showKeyboardShortcuts, showInteractiveTutorial);
this.toolbar.renderModeSelectIcon(
doSwitchMode,
() => doRecordButton(this),
Expand Down Expand Up @@ -6558,6 +6585,17 @@ class Activity {

const activity = new Activity();

// Global function to open First Project Tutorial (starts at card 4)
// Can be called from console or anywhere: openFirstProjectTutorial()
window.openFirstProjectTutorial = function () {
const act = ActivityContext.getActivity();
if (act && act.openFirstProjectTutorial) {
act.openFirstProjectTutorial();
} else if (typeof HelpWidget !== "undefined") {
HelpWidget.openFirstProjectTutorial(act);
}
};

// Execute initialization once all RequireJS modules are loaded AND DOM is ready
define(["domReady!", "activity/exporters"].concat(MYDEFINES), (doc, exportersModule) => {
exporters = exportersModule;
Expand Down
39 changes: 37 additions & 2 deletions js/toolbar-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ class ToolbarUI {
["helpIcon", _("Help and shortcuts")],
["helpGuideItem", _("Help"), "innerHTML"],
["shortcutsGuideItem", _("Keyboard shortcuts"), "innerHTML"],
[
"interactiveTutorialGuideItem",
"<strong>" +
_("Interactive Tutorial") +
"</strong><br/><span style='font-size: 12px; font-weight: normal; color: var(--fg); opacity: 0.7; display: block; margin-top: 4px; line-height: 1.25;'>" +
_(
"Learn Music Blocks by building your first project through a guided interactive walkthrough."
) +
"</span>",
"innerHTML"
],
["runSlowlyIcon", _("Run slowly")],
["runStepByStepIcon", _("Run step by step")],
["displayStatsIcon", _("Display statistics")],
Expand Down Expand Up @@ -275,6 +286,17 @@ class ToolbarUI {
["helpIcon", _("Help and shortcuts")],
["helpGuideItem", _("Help"), "innerHTML"],
["shortcutsGuideItem", _("Keyboard shortcuts"), "innerHTML"],
[
"interactiveTutorialGuideItem",
"<strong>" +
_("Interactive Tutorial") +
"</strong><br/><span style='font-size: 12px; font-weight: normal; color: var(--fg); opacity: 0.7; display: block; margin-top: 4px; line-height: 1.25;'>" +
_(
"Learn Music Blocks by building your first project through a guided interactive walkthrough."
) +
"</span>",
"innerHTML"
],
["runSlowlyIcon", _("Run slowly")],
["runStepByStepIcon", _("Run step by step")],
["displayStatsIcon", _("Display statistics")],
Expand Down Expand Up @@ -1228,11 +1250,13 @@ class ToolbarUI {
* @param {Function} onclick - The onclick handler for the help icon.
* @returns {void}
*/
renderHelpIcon(onclick, shortcutsOnclick) {
renderHelpIcon(onclick, shortcutsOnclick, tutorialOnclick) {
const helpIcon = docById("helpIcon");
const helpGuideItem = docById("helpGuideItem");
const shortcutsGuideItem = docById("shortcutsGuideItem");
const hasDropdownMenu = !!helpGuideItem || !!shortcutsGuideItem;
const interactiveTutorialGuideItem = docById("interactiveTutorialGuideItem");
const hasDropdownMenu =
!!helpGuideItem || !!shortcutsGuideItem || !!interactiveTutorialGuideItem;

if (helpGuideItem) {
helpGuideItem.onclick = event => {
Expand Down Expand Up @@ -1263,6 +1287,17 @@ class ToolbarUI {
onclick(this.activity);
};
}
if (interactiveTutorialGuideItem) {
interactiveTutorialGuideItem.onclick = event => {
if (event) {
event.preventDefault();
event.stopPropagation();
}
if (tutorialOnclick) {
tutorialOnclick(this.activity);
}
};
}
}

/**
Expand Down
13 changes: 13 additions & 0 deletions js/turtledefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,19 @@ const createHelpContent = activity => {
GUIDEURL,
_("Music Blocks Guide")
],
[
_("Build Your First Project"),
_("Ready to explore Music Blocks by building something?") +
" " +
_(
"This guided adventure challenges you to discover blocks, connect ideas, and compose your own melody — step by step."
) +
" " +
_("You'll reflect on what you build along the way.") +
" " +
_("Click the Start Tutorial button below to begin your journey!"),
"data:image/svg+xml;base64," + window.btoa(base64Encode(MOUSEPALETTEICON))
],
[
_("Play"),
_("Click the run button to run the project in fast mode."),
Expand Down
Loading
Loading