Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ce91b3b
hoai2025: dance start over fixes
sanchitmalhotra126 Nov 24, 2025
234c4df
Merge commit 'bb455d5f' into dtl_candidate_bb455d5f
deploy-code-org Nov 25, 2025
811e179
Merge pull request #69771 from code-dot-org/dtl_candidate_bb455d5f
deploy-code-org Nov 25, 2025
02066b4
state transition
sanchitmalhotra126 Nov 25, 2025
06bc8f2
levelbuilder content changes (-robo-commit)
deploy-code-org Nov 26, 2025
ba8f4ba
Merge commit '470e0be0' into dtl_candidate_470e0be0
deploy-code-org Nov 26, 2025
d3dedb9
Merge pull request #69797 from code-dot-org/dtl_candidate_470e0be0
deploy-code-org Nov 26, 2025
7a6af98
Merge pull request #69764 from code-dot-org/sanchit/hoai-dance-start-…
breville Nov 26, 2025
8fc369b
Add "stop preview" button to Web Lab 2 (#69808)
molly-moen Nov 26, 2025
66e2ce0
Update url override for New Music Project button (#69810)
mikeharv Nov 27, 2025
d46fa93
levelbuilder content changes (-robo-commit)
deploy-code-org Nov 27, 2025
95546ea
Merge remote-tracking branch 'origin/levelbuilder' into dts_candidate…
deploy-code-org Nov 27, 2025
f1cac19
Merge pull request #69819 from code-dot-org/dts_candidate_2025-11-27
deploy-code-org Nov 27, 2025
48005fa
Update schema cache dump after schema changes.
deploy-code-org Nov 27, 2025
740b06a
Merge commit 'e335f67c' into dtl_candidate_e335f67c
deploy-code-org Nov 27, 2025
9cd1324
Merge pull request #69820 from code-dot-org/dtl_candidate_e335f67c
deploy-code-org Nov 27, 2025
4e0558a
Merge remote-tracking branch 'origin/levelbuilder' into dts_candidate…
deploy-code-org Nov 28, 2025
9aa09b1
Merge pull request #69822 from code-dot-org/dts_candidate_2025-11-28
deploy-code-org Nov 28, 2025
f92b53a
levelbuilder content changes (-robo-commit)
deploy-code-org Dec 1, 2025
d35e575
Merge remote-tracking branch 'origin/levelbuilder' into dts_candidate…
deploy-code-org Dec 1, 2025
1496cb1
Merge pull request #69824 from code-dot-org/dts_candidate_2025-12-01
deploy-code-org Dec 1, 2025
10bf92d
Implement Hour of AI Code Freeze (#69811)
cat5inthecradle Dec 1, 2025
875b090
Updated cookbook versions
deploy-code-org Dec 1, 2025
efea4d4
move PII scrubber to prod-only (#69784)
carl-codeorg Dec 1, 2025
adaa46b
Updated cookbook versions
deploy-code-org Dec 1, 2025
9234e15
[P5Lab] Preload animations before initializing interpreter (#69809)
mikeharv Dec 1, 2025
d2648ea
hoai2025: share and remix buttons in freeplay header (#69710)
sanchitmalhotra126 Dec 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 9 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
## Warning!!

We have entered Pixel Lock for Hour of AI! All merges to the `staging` branch from Dec 2 through Dec 12 must go through live change review and be deemed critical for supporting the Hour of AI. External contributions will not be accepted at this time.

For non-critical changes, please change your base to `staging-next` and delete this warning. We will merge `staging-next` into `staging` on Dec 15, 2025.

<!-- end warning -->


<!--
A summary of the change, including any relevant background, motivation, and context.
If relevant, include a description, screenshots, and/or video of the existing and new behavior.
-->



## Links
<!-- Links to relevant external resources.
- spec docs, Jira tickets, related PRs, Honeybadger errors, etc.
Expand Down
71 changes: 45 additions & 26 deletions apps/src/codebridge/FilePreview/HTMLPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
DEFAULT_START_HTML_FILE,
} from './constants';
import {HTMLPreviewHeader} from './HTMLPreviewHeader';
import PreviewStopped from './PreviewStopped';

import moduleStyles from './styles/html-preview.module.scss';

Expand Down Expand Up @@ -61,6 +62,7 @@ export const HTMLPreview: React.FC = () => {
const [previewViewMode, setPreviewViewMode] = useState<PreviewViewMode>(
PreviewViewMode.DESKTOP
);
const [isStopped, setIsStopped] = useState<boolean>(false);
const isPredictLevel = levelProperties?.predictSettings?.isPredictLevel;
const hasSubmittedPredictResponse = useAppSelector(
isPredictResponseSubmitted
Expand Down Expand Up @@ -150,9 +152,20 @@ export const HTMLPreview: React.FC = () => {
);
};

const onStopPreview = () => {
setIsStopped(true);
setIsIframeLoaded(false);
};

const onReloadPreview = () => {
setIsStopped(false);
};

useLifecycleNotifier(LifecycleEvent.LevelLoadStarted, () => {
// When we switch levels, clear the source so the preview does not show outdated content.
setDebouncedSource(undefined);
// When we switch levels, reset stopped state.
setIsStopped(false);
setIsLevelLoading(true);
});

Expand Down Expand Up @@ -287,33 +300,39 @@ export const HTMLPreview: React.FC = () => {
onToggleFullScreen={toggleFullScreen}
previewViewMode={previewViewMode}
setPreviewViewMode={setPreviewViewMode}
onStopPreview={onStopPreview}
isStopEnabled={!isStopped}
/>
{/* This iframe points to the environment-specific version of preview.codeprojects.org. That url will eventually
route to InnerHTMLPreview. */}
<div
ref={previewContainerRef}
// This provides a small visual indicator when the iframe is focused after submitting the URL.
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
className={moduleStyles.previewWrapper}
role="application"
aria-label="Web Preview Frame"
>
<iframe
sandbox="allow-scripts allow-same-origin"
allow="self"
title="Web Preview"
ref={iframeRef}
id="preview"
className={classNames(
moduleStyles.previewIframe,
previewViewMode === PreviewViewMode.DESKTOP
? moduleStyles.desktopPreviewIframe
: moduleStyles.mobilePreviewIframe
)}
src={`${previewUrl}${previewQueryString}`}
/>
</div>
{isStopped ? (
<PreviewStopped onReload={onReloadPreview} />
) : (
/* This iframe points to the environment-specific version of preview.codeprojects.org. That url will eventually
route to InnerHTMLPreview. */
<div
ref={previewContainerRef}
// This provides a small visual indicator when the iframe is focused after submitting the URL.
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
className={moduleStyles.previewWrapper}
role="application"
aria-label="Web Preview Frame"
>
<iframe
sandbox="allow-scripts allow-same-origin"
allow="self"
title="Web Preview"
ref={iframeRef}
id="preview"
className={classNames(
moduleStyles.previewIframe,
previewViewMode === PreviewViewMode.DESKTOP
? moduleStyles.desktopPreviewIframe
: moduleStyles.mobilePreviewIframe
)}
src={`${previewUrl}${previewQueryString}`}
/>
</div>
)}
</div>
</PanelContainer>
);
Expand Down
25 changes: 25 additions & 0 deletions apps/src/codebridge/FilePreview/HTMLPreviewHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SegmentedButtons, {
SegmentedButtonsProps,
} from '@code-dot-org/component-library/segmentedButtons';
import TextField from '@code-dot-org/component-library/textField';
import {WithTooltip} from '@code-dot-org/component-library/tooltip';
import classNames from 'classnames';
import React from 'react';

Expand All @@ -25,6 +26,8 @@ interface HTMLPreviewHeaderProps {
onToggleFullScreen: () => void;
previewViewMode: PreviewViewMode;
setPreviewViewMode: (previewViewMode: PreviewViewMode) => void;
onStopPreview: () => void;
isStopEnabled: boolean;
}

export const HTMLPreviewHeader: React.FC<HTMLPreviewHeaderProps> = ({
Expand All @@ -39,6 +42,8 @@ export const HTMLPreviewHeader: React.FC<HTMLPreviewHeaderProps> = ({
onToggleFullScreen,
previewViewMode,
setPreviewViewMode,
onStopPreview,
isStopEnabled,
}) => {
const isFullScreenView = useAppSelector(state => state.lab.isFullScreenView);
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -127,6 +132,26 @@ export const HTMLPreviewHeader: React.FC<HTMLPreviewHeaderProps> = ({
className={moduleStyles.iconButton}
/>
</div>
<WithTooltip
tooltipProps={{
tooltipId: 'stop-preview',
direction: 'onBottom',
size: 'xs',
text: 'Stop preview',
}}
>
<Button
onClick={onStopPreview}
aria-label={'Stop Preview'}
size="xs"
type="secondary"
disabled={!isStopEnabled}
isIconOnly={true}
icon={{iconName: 'circle-stop'}}
className={moduleStyles.iconButton}
color={'destructive'}
/>
</WithTooltip>
<SegmentedButtons
className={moduleStyles.previewViewModeButtons}
{...previewViewModeButtonsProps}
Expand Down
32 changes: 32 additions & 0 deletions apps/src/codebridge/FilePreview/PreviewStopped.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {CodebridgeEmptyState} from '@codebridge/components/CodebridgeEmptyState';
import React, {MouseEvent} from 'react';

import pageStoppedImage from '@cdo/apps/codebridge/images/preview-stopped-placeholder.png';

interface PreviewStoppedProps {
onReload: (
event: MouseEvent<HTMLButtonElement> | MouseEvent<HTMLAnchorElement>
) => void;
}

const PreviewStopped: React.FC<PreviewStoppedProps> = ({onReload}) => {
return (
<div data-theme="Light">
<CodebridgeEmptyState
imageProps={{src: pageStoppedImage}}
title="Preview Stopped"
description="You stopped the preview. If there was an error, review your code or use AI Tutor to help debug before reloading."
buttonProps={{
text: 'Reload Preview',
onClick: onReload,
iconLeft: {iconName: 'sync', iconStyle: 'solid'},
type: 'secondary',
color: 'gray',
size: 's',
}}
/>
</div>
);
};

export default PreviewStopped;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
align-items: center;
justify-content: center;
cursor: default;
background-color: var(--background-neutral-primary);

.emptyStateImage {
width: auto;
Expand Down
4 changes: 4 additions & 0 deletions apps/src/codebridge/components/CodebridgeEmptyState.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {Button, ButtonProps} from '@code-dot-org/component-library/button';
import Image, {ImageProps} from '@code-dot-org/component-library/image';
import {
BodyThreeText,
Expand All @@ -14,13 +15,15 @@ export interface CodebridgeEmptyStateProps {
title?: string;
description?: string;
className?: string;
buttonProps?: ButtonProps;
}

export const CodebridgeEmptyState: FC<CodebridgeEmptyStateProps> = ({
imageProps,
title,
description,
className,
buttonProps,
}) => {
return (
<Box
Expand All @@ -40,6 +43,7 @@ export const CodebridgeEmptyState: FC<CodebridgeEmptyStateProps> = ({
)}
{description && <BodyThreeText>{description}</BodyThreeText>}
</div>
{buttonProps && <Button {...buttonProps} />}
</Box>
);
};
3 changes: 3 additions & 0 deletions apps/src/codebridge/images/preview-stopped-placeholder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 13 additions & 7 deletions apps/src/dance/lab2/views/DanceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ const DanceView: React.FunctionComponent<{
startOver,
} = useSources<DanceProjectSources>();

const sourcesRef = useRef(currentSources);
useEffect(() => {
sourcesRef.current = currentSources;
}, [currentSources]);

const mergeSources = useCallback(
(patch: Partial<DanceProjectSources>, forceSave = false) => {
const next = {...sourcesRef.current, ...patch};
Expand Down Expand Up @@ -468,13 +473,19 @@ const DanceView: React.FunctionComponent<{
return;
}
// In case there is no song set in the current sources, set it to the default.
if (!currentSources.selectedSong) {
if (!currentSources.selectedSong && !usingMusicProject) {
const defaultSong = levelProperties.defaultSong;
const songToUse =
defaultSong && songData[defaultSong] ? defaultSong : songKeys[0];
mergeSources({selectedSong: songToUse});
}
}, [songData, currentSources, mergeSources, levelProperties.defaultSong]);
}, [
songData,
currentSources,
mergeSources,
levelProperties.defaultSong,
usingMusicProject,
]);

// Load the selected song whenever it changes in project sources.
useEffect(() => {
Expand Down Expand Up @@ -579,11 +590,6 @@ const DanceView: React.FunctionComponent<{

const settings = useBlocklySettings();

const sourcesRef = useRef(currentSources);
useEffect(() => {
sourcesRef.current = currentSources;
}, [currentSources]);

if (isShareView) {
const musicMetadata = loadedMusicProject
? musicProjectPlayer.current?.getMetadata()
Expand Down
6 changes: 1 addition & 5 deletions apps/src/dance/lab2/views/GenerateDance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const GenerateDance: React.FunctionComponent<GenerateCodeProps> = ({
if (aiGenerateState === 'none' && blockCount > 1) {
setAiGenerateState('edited');
} else if (
['editing', 'edited'].includes(aiGenerateState) &&
['listened', 'editing', 'edited'].includes(aiGenerateState) &&
blockCount <= 1
) {
resetProgram();
Expand Down Expand Up @@ -352,8 +352,6 @@ const GenerateDance: React.FunctionComponent<GenerateCodeProps> = ({
size="s"
onClick={() => {
startOver();
setAiGenerateState('none');
resetProgram();
analyticsReporter.sendEvent(
EVENTS.DANCE_PARTY_GENERATE_CODE_BACK_TO_PROMPT_CLICKED,
{levelPath: window.location.pathname}
Expand Down Expand Up @@ -438,8 +436,6 @@ const GenerateDance: React.FunctionComponent<GenerateCodeProps> = ({
size="s"
onClick={() => {
startOver();
setAiGenerateState('none');
resetProgram();
analyticsReporter.sendEvent(
EVENTS.DANCE_PARTY_GENERATE_CODE_BACK_TO_PROMPT_CLICKED,
{levelPath: window.location.pathname}
Expand Down
8 changes: 2 additions & 6 deletions apps/src/p5lab/P5Lab.js
Original file line number Diff line number Diff line change
Expand Up @@ -1037,11 +1037,7 @@ export default class P5Lab {
this.p5Wrapper.startExecution();
this.p5Wrapper.setLoop(true);

if (
!this.JSInterpreter ||
!this.JSInterpreter.initialized() ||
this.executionError
) {
if (this.executionError) {
return;
}

Expand Down Expand Up @@ -1219,7 +1215,7 @@ export default class P5Lab {
*/
onP5Preload() {
this.preloadLabAssets()
.then(this.runPreloadEventHandler_())
.then(() => this.runPreloadEventHandler_())
.then(() => this.p5Wrapper.notifyPreloadPhaseComplete());
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions apps/src/templates/projects/NewProjectButtons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react';
import {connect} from 'react-redux';

import fontConstants from '@cdo/apps/fontConstants';
import {studio} from '@cdo/apps/lib/util/urlHelpers';
import {studio, pegasus} from '@cdo/apps/lib/util/urlHelpers';
import i18n from '@cdo/locale';

import styleConstants from '../../styleConstants';
Expand Down Expand Up @@ -121,7 +121,7 @@ const PROJECT_INFO = {
music: {
label: i18n.projectTypeMusic(),
thumbnail: studio('/shared/images/courses/logo_music.png'),
urlOverride: '/s/music-intro-2024/reset',
urlOverride: pegasus('/music'),
},
pythonlab: {
label: i18n.projectTypePythonlab(),
Expand Down
8 changes: 6 additions & 2 deletions aws/cloudformation/drone-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Parameters:
WorkerInstanceAMI:
Type: String
Description: Amazon Machine Image that Autoscaler uses to launch Worker EC2 Instances.
Default: ami-09b347c7ebb552ae2
Default: ami-041891f7c52014278
WorkerInstanceType:
Type: String
Description: EC2 Instance Type that Autoscaler provisions to run Builds.
Expand Down Expand Up @@ -304,7 +304,7 @@ Resources:
-
Name: drone-autoscaler
Essential: true
Image: drone/autoscaler:1.13.0
Image: drone/autoscaler:1.14.0
MemoryReservation: 1024
LogConfiguration:
LogDriver: awslogs
Expand Down Expand Up @@ -407,6 +407,10 @@ Resources:
# END: Settings for the EC2 Instances that Drone Autoscaler provisions.

# BEGIN: Settings for the Drone Runner ("worker"/"agent") executing on the EC2 Instances provisioned by Autoscaler.
-
Name: DRONE_AGENT_IMAGE
# TODO move back to mainline after drone-runners/drone-runner-docker#79 or similar is merged to update Docker Go client
Value: codedotorg/drone-runner-docker
-
Name: DRONE_AGENT_CONCURRENCY
Value: 1
Expand Down
Loading