diff --git a/cypress/e2e/demos-of-tutorials.cy.ts b/cypress/e2e/demos-of-tutorials.cy.ts
index e4b3f53c4..1f8676e14 100644
--- a/cypress/e2e/demos-of-tutorials.cy.ts
+++ b/cypress/e2e/demos-of-tutorials.cy.ts
@@ -5,6 +5,19 @@ context("Demos of all tutorials", () => {
cy.get("ul.tutorial-list li").should("have.length", kExpNTutorials);
}
+ beforeEach(() => {
+ cy.pytchResetDatabase({ initialUrl: "/tutorials/" });
+ assertNTutorials();
+ });
+
+ function launchNthTutorial(tutorialIndex: number) {
+ const childNumber = tutorialIndex + 1;
+ cy.get(
+ `ul.tutorial-list li:nth-child(${childNumber})` +
+ ' button[title="Learn how to make this project"]'
+ ).click();
+ }
+
function launchNthTutorialDemo(tutorialIndex: number) {
const childNumber = tutorialIndex + 1;
cy.get(
@@ -13,10 +26,16 @@ context("Demos of all tutorials", () => {
).click();
}
- it("can run demos", () => {
- cy.pytchResetDatabase({ initialUrl: "/tutorials/" });
- assertNTutorials();
+ it("can start tutorials", () => {
+ for (let tutIdx = 0; tutIdx !== kExpNTutorials; ++tutIdx) {
+ launchNthTutorial(tutIdx);
+ cy.get(".ActivityContent .ProgressTrail").should("be.visible");
+ cy.pytchHomeFromIDE();
+ cy.get(".NavBar li").contains("Tutorials").click();
+ }
+ });
+ it("can run demos", () => {
for (let tutIdx = 0; tutIdx !== kExpNTutorials; ++tutIdx) {
launchNthTutorialDemo(tutIdx);
cy.contains("Click the green flag to run");
diff --git a/cypress/e2e/junior/actor-assets.cy.ts b/cypress/e2e/junior/actor-assets.cy.ts
index 11ece6e7c..db7751757 100644
--- a/cypress/e2e/junior/actor-assets.cy.ts
+++ b/cypress/e2e/junior/actor-assets.cy.ts
@@ -154,6 +154,15 @@ context("Working with assets of an actor", () => {
assertCostumeNames(allCostumes);
});
+ it("asset with uppercase filename", () => {
+ const assetFilename = "RECTANGLE-RED-80-60.PNG";
+
+ selectSprite("Snake");
+ selectActorAspect("Costumes");
+ addFromFixture(assetFilename);
+ assertCostumeNames(["python-logo.png", assetFilename]);
+ });
+
it("has useful UI text for uploading", () => {
const assertContentCorrect = (headerMatch: string, bodyMatch: string) => {
launchAdd.assetFromThisDevice();
diff --git a/cypress/e2e/junior/lesson.cy.ts b/cypress/e2e/junior/lesson.cy.ts
index 6de4da39d..a305f69e7 100644
--- a/cypress/e2e/junior/lesson.cy.ts
+++ b/cypress/e2e/junior/lesson.cy.ts
@@ -1,8 +1,13 @@
import { DiffViewKind, PrettyPrintedLine } from "../../../src/model/code-diff";
import { LinkedJrTutorialRef } from "../../../src/model/junior/jr-tutorial";
-import { assertInIDE, withDownloadedZipfile } from "../utils";
+import {
+ assertInIDE,
+ assertShowsLinkedContentError,
+ withDownloadedZipfile,
+} from "../utils";
import {
assertActorNames,
+ assertJrTutChapterNumber,
assertTwoStateSwitchState,
clickUniqueSelected,
getActivityBarTab,
@@ -73,30 +78,17 @@ context("Navigation of per-method lesson", () => {
).click();
}
- function assertChapterNumber(expNumber: number) {
- if (expNumber === 0) {
- cy.get(".chapter-title").should("be.visible");
- cy.get(".chapter-title .chapter-number").should("not.exist");
- return;
- }
-
- cy.get(".chapter-title .chapter-number").should(
- "have.text",
- `${expNumber} —`
- );
- }
-
it("can move through chapters", () => {
for (let i = 0; i !== 5; ++i) {
advanceToNextChapter(i);
const expChapter = i + 1;
- assertChapterNumber(expChapter);
+ assertJrTutChapterNumber(expChapter);
}
// Jump directly back one at a time until chapter 1.
for (let i = 4; i !== 0; --i) {
jumpToChapter(i);
- assertChapterNumber(i);
+ assertJrTutChapterNumber(i);
}
});
@@ -135,7 +127,7 @@ context("Navigation of per-method lesson", () => {
settleModalDialog("Make a copy");
cy.title().should("match", /Pytch: Copy of/);
- assertChapterNumber(3);
+ assertJrTutChapterNumber(3);
cy.get('.LearnerTask[data-task-index="6"][data-task-kind="current"]');
// help-sidebar, lesson, keynav-help-sidebar
@@ -185,13 +177,13 @@ context("Navigation of per-method lesson", () => {
for (let i = 0; i !== 5; ++i) {
advanceToNextChapter(i);
}
- assertChapterNumber(5);
+ assertJrTutChapterNumber(5);
cy.pytchSwitchProject("LESSON-LINKED-1");
- assertChapterNumber(0);
+ assertJrTutChapterNumber(0);
cy.pytchSwitchProject("LESSON-LINKED-0");
- assertChapterNumber(5);
+ assertJrTutChapterNumber(5);
});
});
@@ -331,7 +323,7 @@ context("Navigation of per-method lesson", () => {
assertHelpVisible();
getActivityBarTab("book").click();
- assertChapterNumber(3);
+ assertJrTutChapterNumber(3);
assertNoHelp();
getActivityBarTab("circle-question").click();
@@ -357,3 +349,20 @@ context("launch demo from tutorial card", () => {
assertActorNames(["Stage", "Bowl", "Apple", "ScoreKeeper"]);
});
});
+
+context("rejects wrong program-kind", () => {
+ beforeEach(() => {
+ cy.pytchResetDatabase();
+ cy.contains("My projects").click();
+ });
+
+ it("flat project but per-method tutorial", () => {
+ cy.pytchTryUploadZipfiles(["v4-flat-linked-to-jr-tutorial.zip"]);
+
+ // The error is caught in two places. To see the message we have to
+ // explicitly select the "lesson" activity.
+ cy.get('button[data-activity-bar-tab="lesson"]').click();
+
+ assertShowsLinkedContentError(/project.*flat.*tutorial.*per-method/);
+ });
+});
diff --git a/cypress/e2e/junior/utils.ts b/cypress/e2e/junior/utils.ts
index ee6a532f8..11dbef624 100644
--- a/cypress/e2e/junior/utils.ts
+++ b/cypress/e2e/junior/utils.ts
@@ -8,7 +8,7 @@ import { deIndent } from "../../common/utils";
import { IconName } from "@fortawesome/fontawesome-common-types";
import { AceControllerMap } from "../../../src/skulpt-connection/code-editor";
-import { launchProjectInListDropdownAction } from "../utils";
+import { assertInIDE, launchProjectInListDropdownAction } from "../utils";
import { Actions } from "easy-peasy";
import { IActiveProject } from "../../../src/model/project";
import { assertNever, range } from "../../../src/utils";
@@ -746,3 +746,21 @@ export const assertTwoStateSwitchState = (
const expStateStr = expState.toString();
cy.get(selector).should("have.attr", "aria-checked", expStateStr);
};
+
+/** Assert that a "per-method" tutorial is being displayed, showing the
+ * chapter with the given `expChapterNumber`. */
+export function assertJrTutChapterNumber(expChapterNumber: number) {
+ assertInIDE("per-method");
+
+ cy.get(".Junior-LessonContent-HeaderBar .chapter-title").as("title");
+
+ if (expChapterNumber === 0) {
+ cy.get("@title").should("be.visible");
+ cy.get("@title").find(".chapter-number").should("not.exist");
+ return;
+ }
+
+ cy.get("@title")
+ .find(".chapter-number")
+ .should("have.text", `${expChapterNumber} —`);
+}
diff --git a/cypress/e2e/keyboard-navigation/global-steer-focus.cy.ts b/cypress/e2e/keyboard-navigation/global-steer-focus.cy.ts
new file mode 100644
index 000000000..c14040754
--- /dev/null
+++ b/cypress/e2e/keyboard-navigation/global-steer-focus.cy.ts
@@ -0,0 +1,234 @@
+import { selectActorAspect, selectSprite, selectStage } from "../junior/utils";
+import { assertInIDE } from "../utils";
+import {
+ activateFlatAsset,
+ assertFocus,
+ KeyOrShortcut,
+ realPress,
+} from "./utils";
+
+context("Global focus steering shortcuts", () => {
+ // These tests are a bit scrappy, ad-hoc, and near-duplicative, but it
+ // didn't quite seem worth the trouble to try to factor out the common
+ // behaviour.
+
+ beforeEach(() => {
+ cy.pytchResetDatabase();
+ });
+
+ function invokeFocusShortcut(key: KeyOrShortcut) {
+ realPress("g");
+ realPress(key);
+ }
+
+ context("flat IDE", () => {
+ it("specimen link", () => {
+ cy.intercept("GET", "**/_by_content_hash_/1234.zip", {
+ fixture: "lesson-specimens/hello-world-lesson.zip",
+ });
+ cy.pytchTryUploadZipfiles(["v4-flat-linked-to-specimen.zip"]);
+ assertInIDE("flat");
+
+ cy.get('button[data-activity-bar-tab="helpsidebar"]').click();
+ realPress("ArrowDown");
+ realPress("Enter");
+
+ invokeFocusShortcut("c");
+ assertFocus("flat-code-editor");
+ realPress("Escape");
+
+ invokeFocusShortcut("h");
+ assertFocus("specimen-info");
+ });
+
+ it("tutorial link", () => {
+ cy.pytchProjectFollowingTutorial();
+ assertInIDE("flat");
+ activateFlatAsset(0);
+
+ invokeFocusShortcut("h");
+ assertFocus("tutorial-content");
+
+ invokeFocusShortcut("a");
+ assertFocus("flat-asset", 0);
+ realPress("ArrowDown", 4);
+ assertFocus("flat-asset", 4);
+
+ invokeFocusShortcut("h");
+ assertFocus("tutorial-content");
+
+ cy.get('button[data-activity-bar-tab="helpsidebar"]')
+ .as("helpButton")
+ .click();
+
+ invokeFocusShortcut("h");
+ assertFocus("help-sidebar", [0]);
+
+ realPress("ArrowDown", 3);
+ realPress("Enter");
+ realPress("ArrowDown", 3);
+ assertFocus("help-sidebar", [3, 2]);
+
+ invokeFocusShortcut("a");
+ assertFocus("flat-asset", 4);
+
+ invokeFocusShortcut("h");
+ assertFocus("help-sidebar", [3, 2]);
+
+ cy.get("@helpButton").click();
+ invokeFocusShortcut("c");
+ assertFocus("flat-code-editor");
+ realPress("Escape");
+ invokeFocusShortcut("h");
+ assertFocus("activity-tab", "helpsidebar");
+
+ realPress("End");
+ assertFocus("activity-tab", "keynavhelp");
+ invokeFocusShortcut("a");
+ assertFocus("flat-asset", 4);
+ invokeFocusShortcut("h");
+ assertFocus("activity-tab", "keynavhelp");
+
+ realPress("Enter");
+ realPress("Tab");
+ assertFocus("keynav-help");
+
+ invokeFocusShortcut("a");
+ assertFocus("flat-asset", 4);
+ invokeFocusShortcut("h");
+ assertFocus("keynav-help");
+ });
+ });
+
+ context("per-method IDE", () => {
+ it("specimen link", () => {
+ cy.intercept("GET", "**/_by_content_hash_/1234.zip", {
+ fixture: "lesson-specimens/per-method-blue-invaders.zip",
+ });
+ cy.pytchTryUploadZipfiles(["v4-jr-linked-to-specimen.zip"]);
+ assertInIDE("per-method");
+
+ selectStage();
+ assertFocus("actor-card", 0);
+
+ cy.get('button[data-activity-bar-tab="helpsidebar"]').click();
+ realPress("ArrowDown");
+ realPress("Enter");
+
+ invokeFocusShortcut("h");
+ assertFocus("specimen-info");
+
+ invokeFocusShortcut("c");
+ assertFocus("add-script-button");
+
+ invokeFocusShortcut("h");
+ assertFocus("specimen-info");
+ });
+
+ it("multiple costumes", () => {
+ cy.pytchTryUploadZipfiles(["pytch-jr-5-costumes-4-sounds.zip"]);
+ assertInIDE("per-method");
+
+ selectSprite("Snake");
+ selectActorAspect("Sounds");
+
+ invokeFocusShortcut("c");
+ assertFocus("sound-card", 0);
+ realPress("ArrowDown", 2);
+ assertFocus("sound-card", 2);
+
+ invokeFocusShortcut("s");
+ assertFocus("actor-card", 1);
+ invokeFocusShortcut("c");
+ assertFocus("sound-card", 2);
+
+ selectActorAspect("Costumes");
+
+ invokeFocusShortcut("c");
+ assertFocus("appearance-card", 0);
+ realPress("ArrowDown", 3);
+ assertFocus("appearance-card", 3);
+
+ invokeFocusShortcut("s");
+ assertFocus("actor-card", 1);
+ invokeFocusShortcut("c");
+ assertFocus("appearance-card", 3);
+ });
+
+ it("tutorial link", () => {
+ cy.pytchTryUploadZipfiles(["v4-jr-linked-to-tutorial.zip"]);
+ assertInIDE("per-method");
+
+ selectStage();
+ assertFocus("actor-card", 0);
+ realPress("ArrowRight");
+ assertFocus("actor-card", 1);
+
+ invokeFocusShortcut("h");
+ assertFocus("tutorial-content");
+
+ invokeFocusShortcut("c");
+ assertFocus("add-script-button");
+
+ invokeFocusShortcut("s");
+ assertFocus("actor-card", 1);
+
+ cy.get('button[data-activity-bar-tab="helpsidebar"]')
+ .as("helpButton")
+ .click();
+
+ invokeFocusShortcut("h");
+ assertFocus("help-sidebar", [0]);
+
+ realPress("ArrowDown", 3);
+ realPress("Enter");
+ realPress("ArrowDown", 3);
+ assertFocus("help-sidebar", [3, 2]);
+
+ invokeFocusShortcut("s");
+ assertFocus("actor-card", 1);
+
+ invokeFocusShortcut("h");
+ assertFocus("help-sidebar", [3, 2]);
+
+ cy.get("@helpButton").click();
+ invokeFocusShortcut("c");
+ assertFocus("add-script-button");
+ invokeFocusShortcut("h");
+ assertFocus("activity-tab", "helpsidebar");
+
+ realPress("End");
+ assertFocus("activity-tab", "keynavhelp");
+ invokeFocusShortcut("c");
+ assertFocus("add-script-button");
+ invokeFocusShortcut("h");
+ assertFocus("activity-tab", "keynavhelp");
+
+ realPress("Enter");
+ realPress("Tab");
+ assertFocus("keynav-help");
+
+ invokeFocusShortcut("c");
+ assertFocus("add-script-button");
+ invokeFocusShortcut("h");
+ assertFocus("keynav-help");
+
+ selectSprite("Snake");
+ selectActorAspect("Costumes");
+ invokeFocusShortcut("c");
+ assertFocus("appearance-card", 0);
+
+ selectActorAspect("Code");
+ invokeFocusShortcut("h");
+ assertFocus("keynav-help");
+ invokeFocusShortcut("c");
+ assertFocus("script", 0);
+
+ selectActorAspect("Sounds");
+ invokeFocusShortcut("h");
+ assertFocus("keynav-help");
+ invokeFocusShortcut("c");
+ assertFocus("add-sound-button");
+ });
+ });
+});
diff --git a/cypress/e2e/keyboard-navigation/utils.ts b/cypress/e2e/keyboard-navigation/utils.ts
index b7bf397d4..27f4ce8c5 100644
--- a/cypress/e2e/keyboard-navigation/utils.ts
+++ b/cypress/e2e/keyboard-navigation/utils.ts
@@ -212,10 +212,12 @@ type FocusableAreaKind =
| "selected-projects-back-button"
| "selected-projects-delete-button"
| "help-sidebar"
+ | "keynav-help"
| "actor-property-tab"
| "info-panel-tab"
| "info-panel-disclosure-toggle"
| "flat-code-tab"
+ | "flat-code-editor"
| "script"
| "script-code"
| "hat-block-option"
@@ -243,6 +245,7 @@ type FocusableAreaKind =
| "stage"
| "progress-node"
| "tutorial-content"
+ | "specimen-info"
| "learner-task-done-button"
| "learner-task-help-button"
| "learner-task-diff-tab"
@@ -292,12 +295,14 @@ export function assertFocus(
export function assertFocus(
area:
+ | "keynav-help"
| "add-project-button"
| "project-new-name-input"
| "selected-projects-back-button"
| "selected-projects-delete-button"
| "info-panel-disclosure-toggle"
| "flat-code-tab"
+ | "flat-code-editor"
| "key-pressed-cancel-button"
| "add-script-button"
| "add-sprite-button"
@@ -310,6 +315,7 @@ export function assertFocus(
| "medialib-filter-switch"
| "medialib-cancel-button"
| "tutorial-content"
+ | "specimen-info"
| "green-flag"
| "stage",
locWithinArea: void
@@ -393,6 +399,9 @@ export function assertFocus(area: FocusableAreaKind, locWithinArea: any): void {
}
}
}
+ case "keynav-help": {
+ return ".KeyNavHelpSidebar";
+ }
case "activity-tab": {
const tabKey = locWithinArea as ActivityBarTabKey;
return `.activity-bar-tabs button[data-activity-bar-tab="${tabKey}"]`;
@@ -421,6 +430,9 @@ export function assertFocus(area: FocusableAreaKind, locWithinArea: any): void {
case "flat-code-tab": {
return ".CodeEditor ul.nav-tabs li:first-child button";
}
+ case "flat-code-editor": {
+ return ".CodeEditor textarea";
+ }
case "script": {
const scriptIdx = locWithinArea as number;
const childIdx1b = scriptIdx + 1;
@@ -563,6 +575,11 @@ export function assertFocus(area: FocusableAreaKind, locWithinArea: any): void {
case "tutorial-content": {
return ".Junior-LessonContent";
}
+ case "specimen-info": {
+ // TODO: Distinguish the various (mis)uses of the
+ // Junior-LessonContent class?
+ return ".Junior-LessonContent";
+ }
default:
return assertNever(area);
}
diff --git a/cypress/e2e/media-library.cy.ts b/cypress/e2e/media-library.cy.ts
index c91b6683d..fdbd2897b 100644
--- a/cypress/e2e/media-library.cy.ts
+++ b/cypress/e2e/media-library.cy.ts
@@ -169,7 +169,7 @@ context("All/just-tut switch", () => {
{
label: "per-method",
tutorialSlug: "script-by-script-boing",
- expNEntries: 4,
+ expNEntries: 5,
preLaunch: () => selectActorAspect("Backdrops"),
},
{
diff --git a/cypress/e2e/notable-change-toasts.cy.ts b/cypress/e2e/notable-change-toasts.cy.ts
index 8bdd09c85..8059fff60 100644
--- a/cypress/e2e/notable-change-toasts.cy.ts
+++ b/cypress/e2e/notable-change-toasts.cy.ts
@@ -30,6 +30,7 @@ type ItShowsToastForDescriptor = {
only?: boolean;
setup: () => void;
submit: () => void;
+ assertCompletion?: () => void;
failurePredicate?: FailurePredicate;
toastBodyMatch: string | RegExp | null;
dismissFun: (gatedDelay: GatedDelay) => void;
@@ -58,6 +59,7 @@ function itShowsToastFor(label: string, descr: ItShowsToastForDescriptor) {
const gatedDelay = GatedDelay.installNew(window);
const dismiss = () => descr.dismissFun(gatedDelay);
descr.submit();
+ descr.assertCompletion?.();
if (descr.failurePredicate != null) {
const failPred = descr.failurePredicate;
cy.get(failPred.selector).contains(failPred.reportMatch);
@@ -118,6 +120,9 @@ context("Toasts are generated (s/b/s)", () => {
cy.get(".CompoundTextInput input").type("{selectAll}{del}two-snakes");
},
submit: () => settleModalDialog("Rename"),
+ assertCompletion: () => {
+ assertFocus("appearance-card", 0);
+ },
toastBodyMatch: 'Costume renamed to "two-snakes.png"',
dismissFun: kDismissSpaceOnCloseButton,
});
diff --git a/cypress/e2e/project-from-specimen.cy.ts b/cypress/e2e/project-from-specimen.cy.ts
index 7b2f628d0..8483b42b2 100644
--- a/cypress/e2e/project-from-specimen.cy.ts
+++ b/cypress/e2e/project-from-specimen.cy.ts
@@ -1,6 +1,7 @@
///
import {
+ assertShowsLinkedContentError,
initSpecimenIntercepts,
kFlatLessonUrl,
kPerMethodLessonUrl,
@@ -360,3 +361,28 @@ context("Compare user code to original", () => {
cy.get(".ViewCodeDiffModal").should("not.exist");
});
});
+
+context("rejects wrong program-kind", () => {
+ beforeEach(() => {
+ cy.pytchResetDatabase();
+ cy.contains("My projects").click();
+ });
+
+ it("flat project but per-method specimen", () => {
+ cy.intercept("GET", "**/_by_content_hash_/1234.zip", {
+ fixture: "lesson-specimens/per-method-blue-invaders.zip",
+ });
+ cy.pytchTryUploadZipfiles(["v4-flat-linked-to-specimen.zip"]);
+
+ assertShowsLinkedContentError(/project.*flat.*specimen.*per-method/);
+ });
+
+ it("per-method project but flat specimen", () => {
+ cy.intercept("GET", "**/_by_content_hash_/1234.zip", {
+ fixture: "lesson-specimens/hello-world-lesson.zip",
+ });
+ cy.pytchTryUploadZipfiles(["v4-jr-linked-to-specimen.zip"]);
+
+ assertShowsLinkedContentError(/project.*per-method.*specimen.*flat/);
+ });
+});
diff --git a/cypress/e2e/start-tutorial-at-chapter.cy.ts b/cypress/e2e/start-tutorial-at-chapter.cy.ts
new file mode 100644
index 000000000..620f63bf2
--- /dev/null
+++ b/cypress/e2e/start-tutorial-at-chapter.cy.ts
@@ -0,0 +1,77 @@
+///
+
+import { assertJrTutChapterNumber, settleModalDialog } from "./junior/utils";
+import { assertInIDE, assertOnFrontPage } from "./utils";
+
+context("Start jr tutorial at chapter", () => {
+ beforeEach(() => {
+ cy.pytchResetDatabase();
+ });
+
+ function assertTutorialNameIncludes(match: string) {
+ cy.get(".TutorialCard.start-at-chapter .card-title").should(
+ "include.text",
+ match
+ );
+ }
+
+ function assertChapterStartContent(expChapterIndex: number) {
+ cy.get(".TutorialCard.start-at-chapter .chapter-index-content").should(
+ "have.text",
+ `Starting at chapter ${expChapterIndex}`
+ );
+ }
+
+ function attemptCreateProject() {
+ cy.get(".TutorialCard.start-at-chapter button").click();
+ }
+
+ function assertError(messageMatch: string) {
+ cy.get(".GenericErrorModal").contains(messageMatch);
+
+ // Dismissing the modal should give the full page structure, but
+ // with error message.
+ settleModalDialog("OK");
+ cy.get(".NavBar.inert");
+ cy.get(".TutorialList");
+ cy.get(".ExceptionDisplay").contains(messageMatch);
+ }
+
+ it("start tutorial at specified chapter", () => {
+ cy.visit("/tutorial-checkpoint/script-by-script-boing/6");
+ assertTutorialNameIncludes("a Pong-like game");
+ assertChapterStartContent(6);
+
+ attemptCreateProject();
+ assertJrTutChapterNumber(6);
+ cy.contains("Bounce the ball off the bats");
+ });
+
+ it("reject invalid tutorial slug", () => {
+ cy.visit("/tutorial-checkpoint/no-such-tutorial/0");
+ assertError("failed to fetch");
+ });
+
+ it("reject invalid chapter index for valid tutorial", () => {
+ cy.visit("/tutorial-checkpoint/script-by-script-boing/42");
+ attemptCreateProject();
+ assertError("chapter 42 not found");
+ });
+
+ it("navigate with replacement", () => {
+ cy.visit("/tutorial-checkpoint/script-by-script-boing/6");
+ attemptCreateProject();
+ assertInIDE("per-method");
+ cy.go("back");
+ assertOnFrontPage();
+ });
+
+ it("navigate home outside router", () => {
+ cy.visit("/tutorial-checkpoint/script-by-script-boing/6");
+ cy.get(".home-link").click();
+ assertOnFrontPage();
+ cy.go("back");
+ assertTutorialNameIncludes("a Pong-like game");
+ assertChapterStartContent(6);
+ });
+});
diff --git a/cypress/e2e/tutorials.cy.ts b/cypress/e2e/tutorials.cy.ts
index 6ce0d1031..fcb069c4d 100644
--- a/cypress/e2e/tutorials.cy.ts
+++ b/cypress/e2e/tutorials.cy.ts
@@ -27,6 +27,8 @@ context("Interact with a tutorial", () => {
cy.pytchProjectFollowingTutorial();
});
+ // This only works when the progress trail display does not include
+ // the ⋯ markers for elided chapter nodes.
const assertActiveChapterIndex = (expActiveIndex: number) => {
cy.get(".ProgressTrail .progress-node-background.isActive").should(
"have.length",
diff --git a/cypress/e2e/upload-zipfile.cy.ts b/cypress/e2e/upload-zipfile.cy.ts
index aff244239..f35079357 100644
--- a/cypress/e2e/upload-zipfile.cy.ts
+++ b/cypress/e2e/upload-zipfile.cy.ts
@@ -42,6 +42,11 @@ context("Upload project from zipfile", () => {
cy.get(".ActorCardContent").should("have.length", 2);
});
+ it("v4 zipfile uppercase asset filename", () => {
+ cy.pytchTryUploadZipfiles(["uppercase-asset-filename.zip"]);
+ cy.get(".ActorCardContent").should("have.length", 2);
+ });
+
it("can upload multiple valid zipfiles", () => {
cy.pytchTryUploadZipfiles([
"hello-world-format-v1.zip",
diff --git a/cypress/e2e/utils.ts b/cypress/e2e/utils.ts
index 31b81f972..027b40b57 100644
--- a/cypress/e2e/utils.ts
+++ b/cypress/e2e/utils.ts
@@ -3,7 +3,7 @@ import { PytchProgramKind } from "../../src/model/pytch-program";
import { assertNever, promiseAndResolve } from "../../src/utils";
export const kExpNTutorials = 18;
-export const kExpNMediaLibEntries = 52;
+export const kExpNMediaLibEntries = 53;
/** Set up request intercepts for a specimen for use in tests. */
export function initSpecimenIntercepts() {
@@ -247,6 +247,11 @@ export function assertCopiedText(match: string | ((text: string) => boolean)) {
);
}
+/** Assert that the webapp is on the front (welcome) page. */
+export function assertOnFrontPage() {
+ cy.get(".welcome-text header .content-text h2").should("have.text", "Pytch");
+}
+
/** Assert that the webapp is on the IDE for a program of the given
* `programKind`, and that the program has finished loading. */
export function assertInIDE(programKind: PytchProgramKind) {
@@ -274,6 +279,16 @@ export function jumpToTutorialChapter(chapterIndex: number) {
cy.get(selector).click();
}
+/** Assuming that the webapp is in the IDE for a project which
+ * supposedly has some linked content, assert that there was in fact an
+ * error loading the content, with technical details matching the given
+ * reg-exp `match`. */
+export function assertShowsLinkedContentError(match: RegExp) {
+ cy.get(".ActivityContent .ErrorMessageDisplay .error-message").contains(
+ match
+ );
+}
+
////////////////////////////////////////////////////////////////////////
export function withDownloadedZipfile(
diff --git a/cypress/e2e/window-title.cy.ts b/cypress/e2e/window-title.cy.ts
index a8d498619..513698dd1 100644
--- a/cypress/e2e/window-title.cy.ts
+++ b/cypress/e2e/window-title.cy.ts
@@ -18,7 +18,7 @@ context("Browser window title", () => {
it("navigate around including tutorials", () => {
cy.visit("/tutorials/");
cy.title().should("eq", "Pytch: Tutorials");
- cy.get(".NavBar").contains("Pytch").click();
+ cy.get(".NavBar").find(".home-link").click();
cy.title().should("eq", "Pytch");
cy.get(".NavBar").contains("My projects").click();
cy.title().should("eq", "Pytch: My projects");
diff --git a/cypress/fixtures/project-zipfiles/make.sh b/cypress/fixtures/project-zipfiles/make.sh
index 937d30405..0c9e4fb71 100755
--- a/cypress/fixtures/project-zipfiles/make.sh
+++ b/cypress/fixtures/project-zipfiles/make.sh
@@ -28,6 +28,13 @@ make_content_v4_jr() {
unzip -q ../simple-v4-jr.zip
}
+make_content_v4_flat() {
+ rm -rf tmp-content
+ mkdir tmp-content
+ cd tmp-content
+ unzip -q ../v4-print-things.zip
+}
+
make_zipfile() {
rm -f ../$1.zip
zip -qr ../$1.zip *
@@ -158,3 +165,30 @@ EOT
EOT
make_zipfile v4-jr-linked-to-specimen
)
+(
+ make_content_v4_flat
+ cat << EOT > meta.json
+{
+ "projectName": "Print some things",
+ "linkedContentRef": {
+ "kind": "specimen",
+ "specimenContentHash": "1234"
+ }
+}
+EOT
+ make_zipfile v4-flat-linked-to-specimen
+)
+(
+ make_content_v4_flat
+ cat << EOT > meta.json
+{
+ "projectName": "Print some things",
+ "linkedContentRef": {
+ "kind": "jr-tutorial",
+ "name": "script-by-script-boing",
+ "interactionState": { "chapterIndex": 0, "nTasksDone": 0 }
+ }
+}
+EOT
+ make_zipfile v4-flat-linked-to-jr-tutorial
+)
diff --git a/cypress/fixtures/project-zipfiles/uppercase-asset-filename.zip b/cypress/fixtures/project-zipfiles/uppercase-asset-filename.zip
new file mode 100644
index 000000000..9201e42ba
Binary files /dev/null and b/cypress/fixtures/project-zipfiles/uppercase-asset-filename.zip differ
diff --git a/cypress/fixtures/project-zipfiles/v4-flat-linked-to-jr-tutorial.zip b/cypress/fixtures/project-zipfiles/v4-flat-linked-to-jr-tutorial.zip
new file mode 100644
index 000000000..f46b47fc2
Binary files /dev/null and b/cypress/fixtures/project-zipfiles/v4-flat-linked-to-jr-tutorial.zip differ
diff --git a/cypress/fixtures/project-zipfiles/v4-flat-linked-to-specimen.zip b/cypress/fixtures/project-zipfiles/v4-flat-linked-to-specimen.zip
new file mode 100644
index 000000000..b94944e72
Binary files /dev/null and b/cypress/fixtures/project-zipfiles/v4-flat-linked-to-specimen.zip differ
diff --git a/cypress/fixtures/project-zipfiles/v4-print-things.zip b/cypress/fixtures/project-zipfiles/v4-print-things.zip
new file mode 100644
index 000000000..86949351c
Binary files /dev/null and b/cypress/fixtures/project-zipfiles/v4-print-things.zip differ
diff --git a/cypress/fixtures/sample-project-assets/RECTANGLE-RED-80-60.PNG b/cypress/fixtures/sample-project-assets/RECTANGLE-RED-80-60.PNG
new file mode 100644
index 000000000..ec4cd1178
Binary files /dev/null and b/cypress/fixtures/sample-project-assets/RECTANGLE-RED-80-60.PNG differ
diff --git a/index.html b/index.html
index 67c2a65d1..3e248d3cc 100644
--- a/index.html
+++ b/index.html
@@ -3,7 +3,7 @@
-
+
=16.0.0"
},
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
@@ -1018,10 +1025,11 @@
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -1054,17 +1062,32 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz",
- "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==",
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
+ "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
+ "@eslint/core": "^0.13.0",
"levn": "^0.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+ "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz",
@@ -1087,6 +1110,18 @@
"node": ">=6"
}
},
+ "node_modules/@fortawesome/free-brands-svg-icons": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-7.1.0.tgz",
+ "integrity": "sha512-9byUd9bgNfthsZAjBl6GxOu1VPHgBuRUP9juI7ZoM98h8xNPTCTagfwUFyYscdZq4Hr7gD1azMfM9s5tIWKZZA==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "7.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.1.0.tgz",
@@ -2200,20 +2235,20 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz",
- "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==",
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.1.tgz",
+ "integrity": "sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.13.0",
- "@typescript-eslint/type-utils": "8.13.0",
- "@typescript-eslint/utils": "8.13.0",
- "@typescript-eslint/visitor-keys": "8.13.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.3.1",
+ "@typescript-eslint/scope-manager": "8.50.1",
+ "@typescript-eslint/type-utils": "8.50.1",
+ "@typescript-eslint/utils": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1",
+ "ignore": "^7.0.0",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2223,26 +2258,187 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
- "eslint": "^8.57.0 || ^9.0.0"
+ "@typescript-eslint/parser": "^8.50.1",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.1.tgz",
+ "integrity": "sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.1.tgz",
+ "integrity": "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.1.tgz",
+ "integrity": "sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.50.1",
+ "@typescript-eslint/tsconfig-utils": "8.50.1",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.1.tgz",
+ "integrity": "sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.50.1",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/typescript-estree": "8.50.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.1.tgz",
+ "integrity": "sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz",
- "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==",
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.1.tgz",
+ "integrity": "sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==",
"dev": true,
+ "license": "MIT",
"peer": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "8.13.0",
- "@typescript-eslint/types": "8.13.0",
- "@typescript-eslint/typescript-estree": "8.13.0",
- "@typescript-eslint/visitor-keys": "8.13.0",
+ "@typescript-eslint/scope-manager": "8.50.1",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/typescript-estree": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1",
"debug": "^4.3.4"
},
"engines": {
@@ -2253,12 +2449,174 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0"
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.1.tgz",
+ "integrity": "sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.1.tgz",
+ "integrity": "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.1.tgz",
+ "integrity": "sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.50.1",
+ "@typescript-eslint/tsconfig-utils": "8.50.1",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.1.tgz",
+ "integrity": "sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.1.tgz",
+ "integrity": "sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.50.1",
+ "@typescript-eslint/types": "^8.50.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.1.tgz",
+ "integrity": "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/scope-manager": {
@@ -2278,16 +2636,35 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.1.tgz",
+ "integrity": "sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz",
- "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==",
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.1.tgz",
+ "integrity": "sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.13.0",
- "@typescript-eslint/utils": "8.13.0",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/typescript-estree": "8.50.1",
+ "@typescript-eslint/utils": "8.50.1",
"debug": "^4.3.4",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2296,10 +2673,163 @@
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.1.tgz",
+ "integrity": "sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.1.tgz",
+ "integrity": "sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.1.tgz",
+ "integrity": "sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.50.1",
+ "@typescript-eslint/tsconfig-utils": "8.50.1",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/visitor-keys": "8.50.1",
+ "debug": "^4.3.4",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.1.tgz",
+ "integrity": "sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.50.1",
+ "@typescript-eslint/types": "8.50.1",
+ "@typescript-eslint/typescript-estree": "8.50.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.50.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.1.tgz",
+ "integrity": "sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.50.1",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
}
},
"node_modules/@typescript-eslint/types": {
@@ -2344,10 +2874,11 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -2915,10 +3446,11 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3033,6 +3565,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@@ -3047,6 +3580,35 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3321,9 +3883,10 @@
"optional": true
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -3690,18 +4253,6 @@
"node": ">=10"
}
},
- "node_modules/cypress-parallel/node_modules/nanoid": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
- "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
- "dev": true,
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
"node_modules/cypress-parallel/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
@@ -3963,11 +4514,12 @@
"integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
},
"node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -3978,6 +4530,12 @@
}
}
},
+ "node_modules/debug/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
"node_modules/decamelize": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
@@ -4012,6 +4570,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@@ -4079,9 +4638,10 @@
"license": "Apache-2.0"
},
"node_modules/diff": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
- "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
@@ -4121,6 +4681,20 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/easy-peasy": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/easy-peasy/-/easy-peasy-6.1.0.tgz",
@@ -4259,12 +4833,10 @@
}
},
"node_modules/es-define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
- "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
- "dependencies": {
- "get-intrinsic": "^1.2.4"
- },
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
}
@@ -4304,10 +4876,10 @@
}
},
"node_modules/es-object-atoms": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
- "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
- "dev": true,
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
@@ -4316,14 +4888,15 @@
}
},
"node_modules/es-set-tostringtag": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
- "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
- "dev": true,
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
"dependencies": {
- "get-intrinsic": "^1.2.4",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
- "hasown": "^2.0.1"
+ "hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -5002,12 +5575,15 @@
}
},
"node_modules/form-data": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
- "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -5112,15 +5688,21 @@
}
},
"node_modules/get-intrinsic": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
- "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
"dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
- "has-proto": "^1.0.1",
- "has-symbols": "^1.0.3",
- "hasown": "^2.0.0"
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -5129,6 +5711,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
@@ -5248,11 +5843,12 @@
}
},
"node_modules/gopd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "dependencies": {
- "get-intrinsic": "^1.1.3"
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -5267,7 +5863,8 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/growl": {
"version": "1.10.5",
@@ -5300,6 +5897,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
"dependencies": {
"es-define-property": "^1.0.0"
},
@@ -5311,6 +5909,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -5319,9 +5918,10 @@
}
},
"node_modules/has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -5333,7 +5933,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
"dependencies": {
"has-symbols": "^1.0.3"
},
@@ -5991,10 +6590,11 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -6430,6 +7030,15 @@
"node": ">= 18"
}
},
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -6517,33 +7126,33 @@
}
},
"node_modules/mocha": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
- "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
+ "version": "10.8.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
+ "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==",
"dev": true,
+ "license": "MIT",
"peer": true,
"dependencies": {
- "ansi-colors": "4.1.1",
- "browser-stdout": "1.3.1",
- "chokidar": "3.5.3",
- "debug": "4.3.4",
- "diff": "5.0.0",
- "escape-string-regexp": "4.0.0",
- "find-up": "5.0.0",
- "glob": "7.2.0",
- "he": "1.2.0",
- "js-yaml": "4.1.0",
- "log-symbols": "4.1.0",
- "minimatch": "5.0.1",
- "ms": "2.1.3",
- "nanoid": "3.3.3",
- "serialize-javascript": "6.0.0",
- "strip-json-comments": "3.1.1",
- "supports-color": "8.1.1",
- "workerpool": "6.2.1",
- "yargs": "16.2.0",
- "yargs-parser": "20.2.4",
- "yargs-unparser": "2.0.0"
+ "ansi-colors": "^4.1.3",
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.5",
+ "diff": "^5.2.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^8.1.0",
+ "he": "^1.2.0",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^5.1.6",
+ "ms": "^2.1.3",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^6.5.1",
+ "yargs": "^16.2.0",
+ "yargs-parser": "^20.2.9",
+ "yargs-unparser": "^2.0.0"
},
"bin": {
"_mocha": "bin/_mocha",
@@ -6551,19 +7160,6 @@
},
"engines": {
"node": ">= 14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mochajs"
- }
- },
- "node_modules/mocha/node_modules/ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
- "dev": true,
- "engines": {
- "node": ">=6"
}
},
"node_modules/mocha/node_modules/argparse": {
@@ -6572,13 +7168,14 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
- "node_modules/mocha/node_modules/diff": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
- "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "node_modules/mocha/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
- "engines": {
- "node": ">=0.3.1"
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
}
},
"node_modules/mocha/node_modules/escape-string-regexp": {
@@ -6594,42 +7191,32 @@
}
},
"node_modules/mocha/node_modules/glob": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
+ "license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
},
"engines": {
- "node": "*"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/mocha/node_modules/glob/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/mocha/node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -6638,10 +7225,11 @@
}
},
"node_modules/mocha/node_modules/minimatch": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
- "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -6649,31 +7237,43 @@
"node": ">=10"
}
},
- "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
"node_modules/mocha/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
+ "node_modules/mocha/node_modules/serialize-javascript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/mocha/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/nanoid": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
- "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"dev": true,
+ "license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -6731,9 +7331,10 @@
}
},
"node_modules/object-inspect": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
- "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -7161,11 +7762,12 @@
}
},
"node_modules/qs": {
- "version": "6.13.1",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz",
- "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==",
+ "version": "6.14.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
+ "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "license": "BSD-3-Clause",
"dependencies": {
- "side-channel": "^1.0.6"
+ "side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
@@ -7377,6 +7979,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-resizable-panels": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.3.1.tgz",
+ "integrity": "sha512-Y+kWgV80snvOvSmWf5ivndOaaxRjaEz/b1snYJaPASqnFzYdfRd6eAXwPPKWtOm/96DNeDGdFssYR8pA8U/zRg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-router": {
"version": "7.9.6",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz",
@@ -8299,6 +8911,7 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
@@ -8351,14 +8964,69 @@
}
},
"node_modules/side-channel": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
- "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
"dependencies": {
- "call-bind": "^1.0.7",
"es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.4",
- "object-inspect": "^1.13.1"
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
@@ -8767,9 +9435,10 @@
"integrity": "sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg=="
},
"node_modules/tmp": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
- "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
+ "license": "MIT",
"engines": {
"node": ">=14.14"
}
@@ -9129,6 +9798,95 @@
}
}
},
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz",
+ "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.13.0",
+ "@typescript-eslint/type-utils": "8.13.0",
+ "@typescript-eslint/utils": "8.13.0",
+ "@typescript-eslint/visitor-keys": "8.13.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz",
+ "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.13.0",
+ "@typescript-eslint/types": "8.13.0",
+ "@typescript-eslint/typescript-estree": "8.13.0",
+ "@typescript-eslint/visitor-keys": "8.13.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": {
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz",
+ "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.13.0",
+ "@typescript-eslint/utils": "8.13.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/unbox-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
@@ -9499,10 +10257,11 @@
}
},
"node_modules/workerpool": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
- "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
- "dev": true
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
+ "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==",
+ "dev": true,
+ "license": "Apache-2.0"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
diff --git a/package.json b/package.json
index 896d50a7d..f6d28ae68 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"@fortawesome/fontawesome-common-types": "^7.1.0",
"@fortawesome/fontawesome-svg-core": "^7.1.0",
+ "@fortawesome/free-brands-svg-icons": "^7.1.0",
"@fortawesome/free-regular-svg-icons": "^7.1.0",
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/react-fontawesome": "^3.1.0",
@@ -28,6 +29,7 @@
"react-error-boundary": "^6.0.0",
"react-image-crop": "^11.0.10",
"react-redux": "^9.2.0",
+ "react-resizable-panels": "^4.3.1",
"react-router-dom": "^7.9.6",
"scratchblocks": "^3.6.2",
"standardized-audio-context": "^25.3.54",
@@ -51,8 +53,8 @@
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
- "@typescript-eslint/eslint-plugin": "^8.13.0",
- "@typescript-eslint/parser": "^8.13.0",
+ "@typescript-eslint/eslint-plugin": "^8.50.1",
+ "@typescript-eslint/parser": "^8.50.1",
"@vitejs/plugin-react": "^5.1.1",
"ajv-cli": "^5.0.0",
"chai": "^4.3.7",
diff --git a/public/favicon.png b/public/favicon.png
index 70dac3218..25a1bd6b9 100644
Binary files a/public/favicon.png and b/public/favicon.png differ
diff --git a/src/App.tsx b/src/App.tsx
index f5b1e19e6..fed56ffcd 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -21,7 +21,7 @@ import "./font-awesome-lib";
import { AllModals } from "./components/AllModals";
import { SingleTutorial } from "./components/SingleTutorial";
import { Link } from "./components/LinkWithinApp";
-import NavBanner from "./components/NavBanner";
+import { NavBanner } from "./components/NavBanner";
import { DemoFromZipfileURL } from "./components/DemoFromZipfileURL";
import { useStoreState, useStoreActions } from "./store";
import { useEffect } from "react";
@@ -31,6 +31,7 @@ import { ProjectFromSpecimenFlow } from "./components/ProjectFromSpecimenFlow";
import { DeliberateFailureWithBoundary } from "./components/DeliberateFailure";
import { fireAndForgetEvent } from "./model/anonymous-instrumentation";
import { StandalonePlayDemo } from "./components/StandalonePlayDemo";
+import { StartTutorialAtCheckpoint } from "./components/StartTutorialAtCheckpoint";
const UnknownRoute: React.FC = () => {
return (
@@ -109,6 +110,10 @@ function App() {
path: "suggested-tutorial/:slug",
element: ,
},
+ {
+ path: "tutorial-checkpoint/:slug/:chapterIndex",
+ element: ,
+ },
{
path: "suggested-demo/:buildId/:demoId",
element: ,
diff --git a/src/components/CaptiveContextMenu.tsx b/src/components/CaptiveContextMenu.tsx
index 68c2579da..8a9882f3c 100644
--- a/src/components/CaptiveContextMenu.tsx
+++ b/src/components/CaptiveContextMenu.tsx
@@ -245,7 +245,7 @@ const DropdownMenu: React.FC> = ({ children }) => {
onKeyDown={onKeydown}
data-captive-context-menu-container-id={ctx.containerId}
>
- ⋮
+
{children}
);
diff --git a/src/components/DecorativeUnderscore.tsx b/src/components/DecorativeUnderscore.tsx
deleted file mode 100644
index c5dbd7574..000000000
--- a/src/components/DecorativeUnderscore.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import React from "react";
-import { EmptyProps } from "../utils";
-
-export const DecorativeUnderscore: React.FC = () => (
- _
-);
diff --git a/src/components/DemoFromZipfileURL.tsx b/src/components/DemoFromZipfileURL.tsx
index a2ebe65f8..61b259d19 100644
--- a/src/components/DemoFromZipfileURL.tsx
+++ b/src/components/DemoFromZipfileURL.tsx
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { demoURLFromId } from "../storage/zipfile";
import { useStoreActions, useStoreState } from "../store";
-import NavBanner from "./NavBanner";
+import { NavBanner } from "./NavBanner";
import Button from "react-bootstrap/Button";
import LoadingOverlay from "./LoadingOverlay";
import { Link } from "./LinkWithinApp";
diff --git a/src/components/EditorAndOutErr.tsx b/src/components/EditorAndOutErr.tsx
index 4c0e3324c..db096b982 100644
--- a/src/components/EditorAndOutErr.tsx
+++ b/src/components/EditorAndOutErr.tsx
@@ -1,11 +1,19 @@
import React from "react";
import classNames from "classnames";
import { useStoreState } from "../store";
-import { useJrEditState } from "./Junior/hooks";
+import { useJrEditActions, useJrEditState } from "./Junior/hooks";
import { EmptyProps, assertNever } from "../utils";
import { CodeEditor } from "./CodeEditor";
import { InfoPanel } from "./Junior/InfoPanel";
import { ActorProperties } from "./Junior/ActorProperties";
+import {
+ Group,
+ Panel,
+ Separator,
+ usePanelRef,
+} from "react-resizable-panels";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { minInfoPanelHeight } from "../constants";
const EditorForProgramKind: React.FC = () => {
const programKind = useStoreState(
@@ -27,11 +35,30 @@ export const EditorAndOutErr: React.FC = () => {
(s) => s.infoPanelState === "collapsed"
);
+ const infoResizablePanelRef = usePanelRef();
+ const toggleStateAction = useJrEditActions((a) => a.toggleInfoPanelState);
+ const isCollapsed = useJrEditState((s) => s.infoPanelState === "collapsed");
+
const classes = classNames("EditorAndOutErr", { infoPanelIsCollapsed });
+
return (
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {
+ console.log('panelSize', panelSize.inPixels);
+ if ((panelSize.inPixels <= minInfoPanelHeight && prevPanelSize?.inPixels > minInfoPanelHeight && !isCollapsed) || panelSize.inPixels > minInfoPanelHeight && prevPanelSize?.inPixels <= minInfoPanelHeight && isCollapsed) {
+ toggleStateAction();
+ }
+ }}>
+
+
+
);
};
diff --git a/src/components/ErrorMessageDisplay.tsx b/src/components/ErrorMessageDisplay.tsx
new file mode 100644
index 000000000..eb153ea0c
--- /dev/null
+++ b/src/components/ErrorMessageDisplay.tsx
@@ -0,0 +1,22 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import React from "react";
+
+type ErrorMessageDisplayProps = { errorMessage: string };
+
+export const ErrorMessageDisplay: React.FC = ({
+ errorMessage,
+}) => {
+ return (
+
+
+
+ Sorry, there was an unexpected problem. Please contact the Pytch team if
+ the problem persists.
+
+
+
Technical details:
+
{errorMessage}
+
+
+ );
+};
diff --git a/src/components/ExceptionDisplay.tsx b/src/components/ExceptionDisplay.tsx
index ce211c2b8..1cb1f8862 100644
--- a/src/components/ExceptionDisplay.tsx
+++ b/src/components/ExceptionDisplay.tsx
@@ -1,10 +1,14 @@
import React from "react";
-import { FallbackProps } from "react-error-boundary";
import { DivSettingWindowTitle } from "./DivSettingWindowTitle";
import Button from "react-bootstrap/Button";
import { envVarOrDefault } from "../env-utils";
+import { ErrorMessageDisplay } from "./ErrorMessageDisplay";
-export function ExceptionDisplay(props: FallbackProps): React.JSX.Element {
+// Accept props of broader type than "FallbackProps" to allow use in
+// other contexts.
+type ExceptionDisplayProps = { error: { message: string } };
+
+export const ExceptionDisplay: React.FC = (props) => {
const { error } = props;
// Use in the below, rather than , to ensure true
@@ -15,14 +19,7 @@ export function ExceptionDisplay(props: FallbackProps): React.JSX.Element {
windowTitle="Pytch: Unexpected error"
>
-
- Sorry, there was an unexpected problem. Please contact the Pytch team
- if the problem persists.
-
-
- (Technical details:{" "}
- {error.message})
-
+
);
-}
+};
diff --git a/src/components/IDELayout.tsx b/src/components/IDELayout.tsx
index da5edadbe..a5e037a86 100644
--- a/src/components/IDELayout.tsx
+++ b/src/components/IDELayout.tsx
@@ -1,6 +1,6 @@
import React, { KeyboardEventHandler, useEffect } from "react";
import classNames from "classnames";
-import { useStoreState } from "../store";
+import { useStoreActions, useStoreState } from "../store";
import { useJrEditState } from "./Junior/hooks";
import { assertNever, EmptyProps } from "../utils";
import { DivSettingWindowTitle } from "./DivSettingWindowTitle";
@@ -13,6 +13,10 @@ import { FlatModals } from "./FlatModals";
import { useFocusContext } from "./hooks/focus-steering";
import { NotableChangeToasts } from "./NotableChangeToasts";
import { useActionAsEffect } from "./hooks/use-action-as-effect";
+import {Group, Panel, PanelSize, Separator} from "react-resizable-panels";
+import {minStageWidth} from "./Junior/WidthMonitor";
+import { stageWidth } from "../constants";
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
const Modals: React.FC
= () => {
const programKind = useStoreState(
@@ -103,6 +107,10 @@ export const IDELayout: React.FC = () => {
}
};
+ const setStageDisplayWidth = useStoreActions(
+ (actions) => actions.ideLayout.setStageDisplayWidth
+ );
+
return (
= () => {
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ //TODO update stage width
+ const targetWidth = Math.min(
+ stageWidth,
+ Math.max(minStageWidth, panelSize.inPixels - 20)
+ );
+ setStageDisplayWidth(targetWidth);
+ })}>
+
+
+
);
diff --git a/src/components/Junior/CodeEditor.tsx b/src/components/Junior/CodeEditor.tsx
index bd6f143fd..487b383e5 100644
--- a/src/components/Junior/CodeEditor.tsx
+++ b/src/components/Junior/CodeEditor.tsx
@@ -144,7 +144,7 @@ const ScriptsEditor = () => {
groupedFocusKey={`ActorProperties/${actorId}/code`}
opts={{ onReorder }}
>
-
+
{maybeNoContentHelp}
{scriptsContent}
diff --git a/src/components/Junior/InfoPanel.tsx b/src/components/Junior/InfoPanel.tsx
index d4bda0aed..b61b7e7d2 100644
--- a/src/components/Junior/InfoPanel.tsx
+++ b/src/components/Junior/InfoPanel.tsx
@@ -8,6 +8,8 @@ import { ErrorReportList } from "./ErrorReportList";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import classNames from "classnames";
import { Button } from "react-bootstrap";
+import { PanelImperativeHandle } from "react-resizable-panels";
+import {minInfoPanelHeight} from "../../constants";
const StandardOutput = () => {
// TODO: Remove duplication between this and non-jr component.
@@ -45,10 +47,26 @@ const Errors = () => {
return
{content}
;
};
-type InfoDisclosureProps = { tabContentId: string };
-const InfoDisclosure: React.FC
= ({ tabContentId }) => {
+type InfoDisclosureProps = {
+ tabContentId: string;
+ resizablePanelRef?:
+ | React.RefObject
+ | undefined;
+};
+const InfoDisclosure: React.FC = ({
+ tabContentId,
+ resizablePanelRef,
+}) => {
+ const isCollapsed = useJrEditState((s) => s.infoPanelState === "collapsed");
const toggleStateAction = useJrEditActions((a) => a.toggleInfoPanelState);
- const toggleState = () => toggleStateAction();
+ const toggleState = () => {
+ console.log('expand panel');
+ if (isCollapsed) {
+ toggleStateAction();
+ resizablePanelRef?.current.expand();
+ if (resizablePanelRef?.current.getSize().inPixels === minInfoPanelHeight) resizablePanelRef.current.resize(380);
+ }
+ };
return (
@@ -68,7 +86,11 @@ const InfoDisclosure: React.FC
= ({ tabContentId }) => {
);
};
-export const InfoPanel = () => {
+interface InfoPanelProps {
+ resizablePanelRef?: React.RefObject;
+}
+
+export const InfoPanel = ({ resizablePanelRef }: InfoPanelProps) => {
const activeTab = useJrEditState((s) => s.infoPanelActiveTab);
const isCollapsed = useJrEditState((s) => s.infoPanelState === "collapsed");
const setActiveTab = useJrEditActions((a) => a.expandAndSetActive);
@@ -76,7 +98,13 @@ export const InfoPanel = () => {
const tabContentId = useId();
const wasCollapsed = useRef(null);
- const toggleState = () => toggleStateAction();
+ const toggleState = () => {
+ console.log('collapse panel');
+ if (!isCollapsed) {
+ toggleStateAction();
+ resizablePanelRef?.current.collapse();
+ }
+ };
const classes = classNames(
"Junior-InfoPanel-container",
@@ -121,7 +149,10 @@ export const InfoPanel = () => {
{isCollapsed ? (
-
+
) : (