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
1,313 changes: 1,236 additions & 77 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"easy-peasy": "^6.1.0",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"marked": "^5.1.0",
"marked": "^17.0.1",
"mime-types": "^2.1.35",
"react": "^19.2.0",
"react-ace": "^14.0.1",
Expand All @@ -27,6 +27,7 @@
"react-dom": "^19.2.0",
"react-error-boundary": "^6.0.0",
"react-image-crop": "^11.0.10",
"react-markdown": "^10.1.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.9.6",
"scratchblocks": "^3.6.2",
Expand Down
18 changes: 18 additions & 0 deletions public/assets/demos/smooth-movement/smooth-movement.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"slug": "",
"description": "Three versions, all using W A S D for movement",
"chapters": [
{
"title": "Basic",
"content": ""
},
{
"title": "Stay on screen",
"content": ""
},
{
"title": "Smoother movement",
"content": ""
}
]
}
6 changes: 6 additions & 0 deletions public/assets/demos/smooth-movement/smooth-movement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Hi, *Pluto*!
Example description
# Hi, Moon!
Example description 2
# Hi, Earth!
Example description 3
37 changes: 37 additions & 0 deletions public/data/demos/demos.json

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions public/data/demos/firing-projectile/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Basic Firing
## Player
The script for the player isn't very important for this example.
Just glide left and right across the bottom of the screen.

## Bullet
### When green start button is pressed
The bullet can be in one of two states. Either we're waiting to be fired, or we're in the middle of being fired up the screen. Keep
track of which state we're in, by remembering whether we're in the middle of being fired. At the start, we're NOT in the middle of
being fired.

While we're waiting to be fired, we don't want to be visible.

### When "space" key is pressed
Once we've started being fired up the screen, we want pressing space
to have no effect. So quit this script before doing any real work
if we are already in the middle of being fired.

Once we get here, we ARE in the middle of being fired. Remember
that fact.

Find the player sprite and move ourselves to its location, except a bit higher ("+20" for the Y coordinate) so the bullet appears at the top of the ship.

Become visible.

Move quite quickly up the screen, until we're well off the top.

We are no longer in the middle of being fired, so remember that.

# Advanced Firing
## Player
The script for the player isn't very important for this example.
Just glide left and right across the bottom of the screen.

## Bullet
### When green start button is pressed
This variable doesn't change, but it's helpful to have a named value
for the maximum number of Bullet clones we want to be allowed to
exist at one time.

### When "space" key is pressed
Make sure that only the original (hidden) Bullet responds to the
keypress. Without this, every clone makes a new clone and we soon
have thousands of clones.

If there are already the maximum number of Bullet clones being fired, stop here.

Make the new Bullet clone; the "when I start as a clone" script below does the rest of the work.

### When bullet starts as a clone
Find the player sprite and move ourselves to its location, except a bit higher ("+20" for the Y coordinate) so the bullet appears at the top of the ship.

Become visible.

Move quite quickly up the screen, until we're well off the top.

This clone has finished its job, so delete it to avoid cluttering up
the game with lots of clones.
Binary file added public/data/demos/firing-projectile/project.zip
Binary file not shown.
31 changes: 31 additions & 0 deletions public/data/demos/jumping/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# _Basic Jumping_
Thanks [GrafxKid](https://opengameart.org/content/green-robot) for costume!

Start off in the middle left/right, and quite near the bottom,
to stand on the earth.

We need to remember whether we're currently in the middle of a jump
or not. We start NOT in the middle of a jump.

Continuously check for left/right keys being pressed, and adjust our
X coordinate if needed. This simple version does not check for
where the player is on the screen, so you can move the ship right
outside the screen. It does choose a different costume for moving
left vs moving right, though, to look better.

If we're already in the middle of jumping, quit this script
immediately, so the player can't stack jumps.

Remember that we ARE now jumping.

# Smooth Jumping

To get a smooth jump, start by moving up quite quickly, then reduce
how much we move up every frame. At some point, the y_velocity
variable will become NEGATIVE, meaning we will move DOWN. We stop
this whole process just after we've done the movement with a velociy
equal to the negative of the starting velocity, when we will be
vertically back where we started.

Record the fact that we have finished jumping, and so it's OK to
jump again next time the player presses space.
Binary file added public/data/demos/jumping/project.zip
Binary file not shown.
35 changes: 35 additions & 0 deletions public/data/demos/smooth-movement/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Version 1
Keep the speed in a variable. Even though we don't change the value, giving it a name makes the code easier to read and easier to
change.

Continuously check for movement keys being pressed, and adjust our X
or Y coordinate if needed. This simple version does not check for
where the player is on the screen, so you can move the ship right
outside the screen.

# Version 2
## `x_limit`, `y_limit`
Likewise, keep the X and Y coordinate limits (the furthest we want the player to be able to go left and right, and up
and down) in variables.

# Version 3
## `x_velocity`, `y_velocity`
These variables DO change as the player plays the game. They track
the current speed of the player in each dimension.

## `accel`
This variable doesn't change value, but (as above) it's useful to
give it a name. This is the acceleration, which says how quickly
the player's speed changes.

### Drift
Continuously check for movement keys being pressed, and adjust our X
or Y velocity if needed, up to the "speed" limit. <mark>If neither
left/right key is pressed, drift the horizontal velocity
(x_velocity) towards zero. And if neither up/down key is pressed,
drift the vertical velocity (y_velocity) towards zero. Then update
the position according to the velocity, making sure we don't go
outside the screen limits.</mark>

It might also be nice to eventually have a pytch filetype if the project structure become more complex and we ever ended
up needing different filetypes for different purposes (like maybe for raspberry pi projects or )
Binary file added public/data/demos/smooth-movement/project.zip
Binary file not shown.
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import "./pytch-ide.scss";
import "./pytch-jr-ide.scss";
import "./pytch-jr-lesson.scss";
import "./help-sidebar.scss";
import "./demo-sidebar.scss";
import "./font-awesome-lib";

import { AllModals } from "./components/AllModals";
Expand All @@ -32,6 +33,7 @@ import { DeliberateFailureWithBoundary } from "./components/DeliberateFailure";
import { fireAndForgetEvent } from "./model/anonymous-instrumentation";
import { StandalonePlayDemo } from "./components/StandalonePlayDemo";
import { StartTutorialAtCheckpoint } from "./components/StartTutorialAtCheckpoint";
import { DemoList } from "./components/discoverable-demos-page/DemoList";

const UnknownRoute: React.FC<EmptyProps> = () => {
return (
Expand Down Expand Up @@ -98,6 +100,10 @@ function App() {
path: "tutorials/",
element: <TutorialList />,
},
{
path: "demos/",
element: <DemoList />,
},
{
path: "ide/:projectIdString",
element: <IDE />,
Expand Down
18 changes: 18 additions & 0 deletions src/assets/demos/smooth-movement/smooth-movement.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"slug": "",
"description": "Three versions, all using W A S D for movement",
"chapters": [
{
"title": "Basic",
"content": ""
},
{
"title": "Stay on screen",
"content": ""
},
{
"title": "Smoother movement",
"content": ""
}
]
}
11 changes: 11 additions & 0 deletions src/assets/demos/smooth-movement/smooth-movement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Hi, *Pluto*! asdasdadasdsadadadsdasdasdsadsadsadasdadasdadadsda

Example descriptionsbdvsnlnkdkslnnklsvklnsdvknlsdvklnsdvnksdvnlksdvklnsdvklnsdvndvsklndsvknsdvklnsdvkn
# Hi, Moon!
Textnlsdndkldsvknsdvnklsdvnlkdvnlsdvklnsdvklnsklsdlvsdklvlksvd dsvnklnsvdkv sdvndsbsd vdsv snvdkslvklndsvkldkv
## Example Subheading
Example description 2mvsdnlksdvnklsdvnklsdvldsvlksdvnnk vsdvnvdslnvsd dsvnsdbvks sdvdvsvibvsdbvdsklvdnsd svkdnvls
# HI
hi.vsklnkvvskldsvklnsdvklnsdvknsdvklsdkldkvnjrnlteobjtioebnk nanöldvölmvpa vodabhpvnal vmdajp nblojehpbw
# HEY
hiya.nlkv dvsnbv vsldvndsvbsd vs,nwbo envk wbebw ewlbnw e
1 change: 1 addition & 0 deletions src/components/IDELayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const IDELayout: React.FC<EmptyProps> = () => {
case "expanded-specimen":
case "expanded-lesson":
case "expanded-tutorial":
case "expanded-demo":
return 1;
case "expanded-keynavhelp":
console.warn("should not have expanded-keynavhelp on first render");
Expand Down
6 changes: 5 additions & 1 deletion src/components/Junior/ActivityBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useJrEditActions, useJrEditState } from "./hooks";
import classNames from "classnames";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconName } from "@fortawesome/fontawesome-common-types";
import { useHasLinkedLesson, useHasLinkedSpecimen } from "./lesson/hooks";
import { useHasLinkedDemo, useHasLinkedLesson, useHasLinkedSpecimen } from "./lesson/hooks";
import { EmptyProps } from "../../utils";
import { useStoreState } from "../../store";
import { Nav } from "react-bootstrap";
Expand All @@ -23,6 +23,7 @@ const uiDetailsFromTabKeyLut = new Map<ActivityBarTabKey, TabKeyUiDetails>([
["lesson", { icon: "book", tooltip: "Lesson content" }],
["tutorial", { icon: "book", tooltip: "Tutorial content" }],
["specimen", { icon: "book", tooltip: "Lesson information" }],
["demo", { icon: "play", tooltip: "Demo content" }],
]);

function uiDetailsFromTabKey(tab: ActivityBarTabKey): TabKeyUiDetails {
Expand Down Expand Up @@ -86,13 +87,16 @@ export const ActivityBar: React.FC<EmptyProps> = () => {
const hasLinkedTutorial = useStoreState(
(state) => state.activeProject.project?.trackedTutorial != null
);
const hasLinkedDemo = useHasLinkedDemo();

const tabs: Array<ActivityBarTabKey> = hasLinkedLesson
? ["helpsidebar", "lesson", "keynavhelp"]
: hasLinkedSpecimen
? ["helpsidebar", "specimen", "keynavhelp"]
: hasLinkedTutorial
? ["helpsidebar", "tutorial", "keynavhelp"]
: hasLinkedDemo
? ["helpsidebar", "demo", "keynavhelp"]
: ["helpsidebar", "keynavhelp"];

const focusGroupExtraClass =
Expand Down
10 changes: 9 additions & 1 deletion src/components/Junior/ActivityContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { WidthMonitor } from "./WidthMonitor";
import { HelpSidebar } from "../HelpSidebar";
import Tutorial from "../Tutorial";
import { KeyNavHelpSidebar } from "./KeyNavHelpSidebar";
import { DemoSidebar } from "../demo-sidebar/DemoSidebar";

export const ActivityContent: React.FC<EmptyProps> = () => {
const s = useJrEditState((s) => s.activityContentState);
Expand All @@ -16,6 +17,13 @@ export const ActivityContent: React.FC<EmptyProps> = () => {

const content = (() => {
switch (s.tab) {
case "demo":
return (
<>
<WidthMonitor nonStageWd={980} />
<DemoSidebar />
</>
);
case "helpsidebar":
return (
<>
Expand All @@ -24,7 +32,7 @@ export const ActivityContent: React.FC<EmptyProps> = () => {
</>
);
case "keynavhelp":
return <KeyNavHelpSidebar />;
return <KeyNavHelpSidebar />;
case "lesson":
case "specimen":
// This is a bit of a fudge. We treat these both as "lesson"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Junior/InfoPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const InfoDisclosure: React.FC<InfoDisclosureProps> = ({ tabContentId }) => {
const toggleState = () => toggleStateAction();

return (
<div>
<div className={"h-100 d-flex"}>
<Button
variant="outline-secondary"
size="sm"
Expand Down
5 changes: 2 additions & 3 deletions src/components/Junior/KeyNavHelpSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { marked } from "marked";
import { markedParse } from "../hooks/sync-marked";
import { assertNever, EmptyProps } from "../../utils";
import { Row, Col, Container, Spinner } from "react-bootstrap";
import {
Expand Down Expand Up @@ -66,8 +66,7 @@ const Key: React.FC<{ keyDescr: KeyDescriptor }> = ({ keyDescr }) => {
};

const TextContent: React.FC<{ markdown: string }> = ({ markdown }) => {
marked.use({ mangle: false, headerIds: false });
const html = marked.parse(markdown);
const html = markedParse(markdown);
return <div dangerouslySetInnerHTML={{ __html: html }} />;
};

Expand Down
6 changes: 5 additions & 1 deletion src/components/Junior/lesson/MaybeContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { Content } from "./Content";
import { ContentLoadingSpinner } from "./ContentLoadingSpinner";
import { SpecimenInformation } from "./SpecimenInformation";
import { ErrorMessageDisplay } from "../../ErrorMessageDisplay";
import { DemoSidebar } from "../../demo-sidebar/DemoSidebar";

export const MaybeContent: React.FC<EmptyProps> = () => {
const linkedContentState = useLinkedContentLoadingStateSummary();

console.log("linkedContentstate", linkedContentState);
switch (linkedContentState.kind) {
case "idle":
return null;
Expand All @@ -22,6 +23,8 @@ export const MaybeContent: React.FC<EmptyProps> = () => {
return <Content />;
case "specimen":
return <SpecimenInformation />;
case "demo":
return <DemoSidebar />;
default:
return assertNever(contentKind);
}
Expand All @@ -40,6 +43,7 @@ export const MaybeContent: React.FC<EmptyProps> = () => {
return null;
case "jr-tutorial":
case "specimen":
case "demo":
return <ContentLoadingSpinner />;
default:
return assertNever(contentKind);
Expand Down
Loading