Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Launch Terminal Instructions experiment for Shell stage 1 and 2 #2521

Merged
merged 8 commits into from
Jan 10, 2025
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
<CoursePage::InstructionsCard @title="How to pass this stage" id="first-stage-tutorial-card" ...attributes>
<:content>
<div class="prose dark:prose-invert mb-5">
<p>
Since this is the first stage, we've included some commented code to help you get started. To pass this stage, simply uncomment the code and
submit your changes.
</p>
{{#if (and (eq @repository.course.slug "shell") this.shouldShowTerminalInstructions)}}
<p>
In this stage, you'll implement printing a shell prompt
<code>"$ "</code>
and waiting for user input.
</p>
<div class="mb-6 bg-slate-800 dark:bg-slate-100 rounded-xl max-w-md shadow-md dark:shadow-slate-600">
<div class="flex items-center h-14 px-5 gap-2.5">
<div class="size-3 bg-slate-500 dark:bg-slate-300 rounded-full flex-shrink-0"></div>
<div class="size-3 bg-slate-500 dark:bg-slate-300 rounded-full flex-shrink-0"></div>
<div class="size-3 bg-slate-500 dark:bg-slate-300 rounded-full flex-shrink-0"></div>
</div>
<div class="pt-0 p-5">
<button
type="button"
class="flex items-center gap-2 w-fit {{if this.shouldShowTerminalInstructionsPinging 'cursor-pointer' 'cursor-default'}}"
{{on "click" this.handleTerminalClick}}
>
<span class="font-mono text-slate-100 dark:text-slate-800">$</span>
<div class="relative flex size-6">
{{#if this.shouldShowTerminalInstructionsPinging}}
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"></span>
{{/if}}
{{svg-jar "question-mark-circle" class="size-6 text-blue-500"}}
</div>
<EmberTooltip @side="right" @text='Print "$ " to pass this stage.' @popperContainer="body" />
</button>
</div>
</div>
{{else}}
<p>
Since this is the first stage, we've included some commented code to help you get started. To pass this stage, simply uncomment the code and
submit your changes.
</p>
{{/if}}
</div>

<ExpandableStepList @steps={{this.steps}} @onManualStepComplete={{this.handleStepCompletedManually}} class="scroll-mt-32" as |stepList|>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type CourseStageStep from 'codecrafters-frontend/utils/course-page-step-list/course-stage-step';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';

interface Signature {
Element: HTMLDivElement;
Expand Down Expand Up @@ -73,6 +74,8 @@
@service declare featureFlags: FeatureFlagsService;
@service declare store: Store;

@tracked isTerminalInstructionsPingingClicked = false;

Check warning on line 78 in app/components/course-page/course-stage-step/first-stage-tutorial-card.ts

View check run for this annotation

Codecov / codecov/patch

app/components/course-page/course-stage-step/first-stage-tutorial-card.ts#L78

Added line #L78 was not covered by tests
get hasPassedTests() {
return this.args.currentStep.testsStatus === 'passed' || this.args.currentStep.status === 'complete';
}
Expand All @@ -85,6 +88,14 @@
return this.coursePageState.manuallyCompletedStepIdsInFirstStageInstructions.includes('navigate-to-file');
}

get shouldShowTerminalInstructions() {
return this.featureFlags.canSeeTerminalInstructionsForStage1And2;
}

Check warning on line 93 in app/components/course-page/course-stage-step/first-stage-tutorial-card.ts

View check run for this annotation

Codecov / codecov/patch

app/components/course-page/course-stage-step/first-stage-tutorial-card.ts#L93

Added line #L93 was not covered by tests

get shouldShowTerminalInstructionsPinging() {
return !this.hasPassedTests && !this.isTerminalInstructionsPingingClicked;
}

get steps() {
return [
new NavigateToFileStep(this.args.repository, this.navigateToFileStepIsComplete),
Expand Down Expand Up @@ -129,6 +140,11 @@
}
}

@action
handleTerminalClick() {
this.isTerminalInstructionsPingingClicked = true;
}

Check warning on line 146 in app/components/course-page/course-stage-step/first-stage-tutorial-card.ts

View check run for this annotation

Codecov / codecov/patch

app/components/course-page/course-stage-step/first-stage-tutorial-card.ts#L146

Added line #L146 was not covered by tests

@action
handleViewLogsButtonClick() {
this.coursePageState.testResultsBarIsExpanded = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,43 @@
<:content>
<div class="prose dark:prose-invert mb-5">
{{#if (eq @repository.course.slug "shell")}}
<div class="mb-5 prose-compact">
{{markdown-to-html @courseStage.shortInstructionsMarkdown}}
</div>
{{#if this.shouldShowTerminalInstructions}}
<p>
In this stage, you'll implement support for handling invalid commands in your shell.
</p>
<div class="mb-6 bg-slate-800 dark:bg-slate-100 rounded-xl max-w-md shadow-md dark:shadow-slate-600">
<div class="flex items-center h-14 px-5 gap-2.5">
<div class="size-3 bg-slate-500 dark:bg-slate-300 rounded-full flex-shrink-0"></div>
<div class="size-3 bg-slate-500 dark:bg-slate-300 rounded-full flex-shrink-0"></div>
<div class="size-3 bg-slate-500 dark:bg-slate-300 rounded-full flex-shrink-0"></div>
</div>
<div class="pt-0 p-5 font-mono">
<div class="flex items-center gap-2 mb-2 text-slate-300 dark:text-slate-600 cursor-default w-fit">$
<span class="text-red-400">invalid_command</span>
<EmberTooltip @side="right" @text="Treat every command as invalid for now." @popperContainer="body" />
</div>
<button
type="button"
class="flex items-center gap-2 text-slate-100 dark:text-slate-800 w-fit
{{if this.shouldShowTerminalInstructionsPinging 'cursor-pointer' 'cursor-auto'}}"
{{on "click" this.handleTerminalClick}}
>
invalid_command: command not found
<span class="relative flex size-6">
{{#if this.shouldShowTerminalInstructionsPinging}}
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"></span>
{{/if}}
{{svg-jar "question-mark-circle" class="size-6 text-blue-500"}}
</span>
<EmberTooltip @side="right" @text="Read the command and print the error message." @popperContainer="body" />
</button>
</div>
</div>
{{else}}
<div class="mt-2 mb-5 prose-compact">
{{markdown-to-html @courseStage.shortInstructionsMarkdown}}
</div>
{{/if}}
{{else}}
<p>
In this stage, you'll implement your own solution. Unlike stage 1, your repository doesn't contain commented code to pass this stage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type FeatureFlagsService from 'codecrafters-frontend/services/feature-flags';
import { action } from '@ember/object';
import type { Step } from 'codecrafters-frontend/components/expandable-step-list';
import { tracked } from '@glimmer/tracking';

interface Signature {
Element: HTMLDivElement;
Expand Down Expand Up @@ -54,6 +55,8 @@
@service declare featureFlags: FeatureFlagsService;
@service declare store: Store;

@tracked isTerminalInstructionsPingingClicked = false;

Check warning on line 59 in app/components/course-page/course-stage-step/second-stage-tutorial-card.ts

View check run for this annotation

Codecov / codecov/patch

app/components/course-page/course-stage-step/second-stage-tutorial-card.ts#L59

Added line #L59 was not covered by tests
get implementSolutionStepIsComplete() {
return (
this.implementSolutionStepWasMarkedAsComplete ||
Expand Down Expand Up @@ -83,6 +86,14 @@
);
}

get shouldShowTerminalInstructions() {
return this.featureFlags.canSeeTerminalInstructionsForStage1And2;
}

Check warning on line 91 in app/components/course-page/course-stage-step/second-stage-tutorial-card.ts

View check run for this annotation

Codecov / codecov/patch

app/components/course-page/course-stage-step/second-stage-tutorial-card.ts#L91

Added line #L91 was not covered by tests

get shouldShowTerminalInstructionsPinging() {
return !this.runTestsStepIsComplete && !this.isTerminalInstructionsPingingClicked;
}

get steps() {
return [
new ImplementSolutionStep(this.args.repository, this.implementSolutionStepIsComplete),
Expand Down Expand Up @@ -114,6 +125,11 @@
}
}

@action
handleTerminalClick() {
this.isTerminalInstructionsPingingClicked = true;
}

Check warning on line 131 in app/components/course-page/course-stage-step/second-stage-tutorial-card.ts

View check run for this annotation

Codecov / codecov/patch

app/components/course-page/course-stage-step/second-stage-tutorial-card.ts#L131

Added line #L131 was not covered by tests

@action
handleViewLogsButtonClick() {
this.coursePageState.testResultsBarIsExpanded = true;
Expand Down
4 changes: 4 additions & 0 deletions app/services/feature-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
return this.currentUser && (this.currentUser.isStaff || this.currentUser.isConceptAuthor);
}

get canSeeTerminalInstructionsForStage1And2() {
return this.currentUser?.isStaff || this.getFeatureFlagValue('can-see-terminal-instructions-for-stage-1-and-2') === 'test';

Check warning on line 18 in app/services/feature-flags.js

View check run for this annotation

Codecov / codecov/patch

app/services/feature-flags.js#L18

Added line #L18 was not covered by tests
}

get currentUser() {
return this.authenticator.currentUser;
}
Expand Down
2 changes: 1 addition & 1 deletion types/glint.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ declare module '@glint/environment-ember-loose/registry' {
capitalize: HelperLike<{ Return: string; Args: { Positional: [string] } }>;
'did-resize': ModifierLike<{ Args: { Positional: [(entry: ResizeObserverEntry) => void] } }>;
EmberTooltip: ComponentLike<{
Args: { Named: { text?: string; side?: 'top' | 'bottom' | 'left' | 'right'; delay?: number; duration?: number } };
Args: { Named: { text?: string; side?: 'top' | 'bottom' | 'left' | 'right'; delay?: number; duration?: number; popperContainer?: string } };
Blocks: { default?: [] };
}>;
EmberPopover: ComponentLike<{
Expand Down
Loading