[Demo PR] Multi-sprite Tutorial Mode UI Design - #3429
Draft
wenjiangping wants to merge 148 commits into
Draft
wenjiangping wants to merge 148 commits into
wenjiangping wants to merge 148 commits into
Conversation
…eries - Show tutorial-specific copy in the leave-editor confirm dialog when a course is active; keep the original copy otherwise - Make the course success modal's "back to course series" button navigate to the current course's series instead of /tutorials Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a one-shot skip flag on Tutorial so explicit, expected navigations away from the editor do not trigger the leave confirmation: - "Back to course series" in the success modal requests the skip before navigating to the series page - startCourse requests the skip before pushing the course entrypoint, which may point to a non-editor route Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A plain one-shot flag could leak: navigations that don't reach the editor's onBeforeRouteLeave (e.g. editor -> editor route updates when starting the next course, or starting a course from a non-editor page) never consume it, so a later genuine leave would silently skip its confirmation. Expire the skip request after skipLeaveConfirmTimeout instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Request the leave-confirm skip before emit('cancelled') so the time-bound
window isn't shortened by the cancel handler
- Wrap "back to course series" navigation in useMessageHandle to surface
router.push rejections, consistent with handleStartNextCourse
- Use sentence case for the button label: "Back to series courses"
- Align the tutorial.ts comment with the actual button label
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Invert the dependency so the editor no longer needs tutorial knowledge. Add an editor-owned `editorLeaveConfirm` controller exposing a generic message override and a one-shot, time-bound skip. Tutorial drives it the same way it drives copilot (TutorialRoot sets the tutorial-specific copy while a course is active; startCourse and the success modal request the skip), and the editor page's leave guard only reads the controller. The editor stays unaware of tutorials (no components/editor -> tutorials import), and pages/editor no longer imports useTutorial. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instead of auto-applying patches or simply point to the editor area using a purple arrow, Copilot now guides the user to make the
edit themselves in the editor, so children practice writing code by hand.
A reply can render four guide elements(custom elements), each backed by an in-editor overlay:
- [NEW] code-drag-hint — opens a drop target at a line and highlights the matching
API reference item, for inserting a new statement by drag
- [NEW] code-type-hint — opens a pre-indented blank line with a "Type the code here"
chip and a green reference ghost, for typing a new statement
- [NEW] code-delete-hint — highlights a range in red, for deleting it
- [MODIFIED] code-change-hint — shows the deletion (red) and addition (green) at once, for
replacing existing code (inline or whole-line)
The CodeGuideController keeps a single active guide, navigates to it, and
auto-dismisses it once the suggested edit is done (whitespace-insensitive).
Completion for delete/change is region-local: an invisible Monaco decoration
tracks the target range so it stays correct even when the user edits elsewhere.
Shared logic lives in use-code-guide.ts (re-show on stream settle, reopen on
click, skip-if-already-done, blank-line scaffolding) so all elements behave
consistently.
Changes to be committed:
new file: node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json
modified: spx-gui/src/components/copilot/CopilotRound.vue
modified: spx-gui/src/components/copilot/context.ts
modified: spx-gui/src/components/copilot/copilot.ts
modified: spx-gui/src/components/editor/copilot/CodeChange.vue
new file: spx-gui/src/components/editor/copilot/CodeDeleteHint.vue
new file: spx-gui/src/components/editor/copilot/CodeDragHint.vue
new file: spx-gui/src/components/editor/copilot/CodeTypeHint.vue
modified: spx-gui/src/components/editor/copilot/index.ts
new file: spx-gui/src/components/editor/copilot/use-code-guide.ts
modified: spx-gui/src/components/xgo-code-editor/index.ts
modified: spx-gui/src/components/xgo-code-editor/ui/CodeEditorUI.vue
modified: spx-gui/src/components/xgo-code-editor/ui/api-reference/APIReferenceItem.vue
modified: spx-gui/src/components/xgo-code-editor/ui/api-reference/index.ts
modified: spx-gui/src/components/xgo-code-editor/ui/code-editor-ui.ts
new file: spx-gui/src/components/xgo-code-editor/ui/code-guide/CodeGuideUI.vue
new file: spx-gui/src/components/xgo-code-editor/ui/code-guide/index.test.ts
new file: spx-gui/src/components/xgo-code-editor/ui/code-guide/index.ts
modified: spx-gui/src/components/xgo-code-editor/ui/common.ts
…rials Let a tutorial course focus the "API References" panel on just the APIs it uses, so learners are not distracted by the full catalog. - Generic editor: add an `apiReferenceFilter` predicate on CodeEditor; the API reference controller derives the shown items from it synchronously, so the panel updates reactively without re-running the async provider. - Copilot: an `api-reference-filter` custom element narrows the panel to a set of API definition ids, and a `list_api_reference_items` tool lets the copilot look up the exact ids to use. - Tutorial: the element is registered only while a course is active and the filter is reset when the course ends; the course prompt instructs the copilot to narrow the panel to the APIs the whole course uses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only the success modal's course navigations leave the editor as an explicit, expected action; other startCourse callers (course-start / course-series pages) are not leaving an editor. Request the skip at the success modal call site instead of inside startCourse, so it no longer applies to every caller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ride Use a single leave-confirmation message for all effect-free cases instead of a tutorial-specific one. The copy also drops the "if you leave now" clause, which could imply the edits are saveable later. editorLeaveConfirm now only exposes the one-shot skip, and the tutorial layer no longer drives the confirmation copy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ng, navbar course entry & story video During a tutorial course the editor switches to a focused workspace layout: the game preview takes ~48% of the workspace (min-width 660px), run/rerun/stop float at the bottom-right corner of the preview, and code is rendered with a larger fixed font. Which workspace areas get hidden (resource panels, edit-mode switch, preview header, code editor tools) is decided by the copilot via a new <workspace-hidden-areas> custom element, following the api-reference-filter pattern: the editor exposes a generic workspace-layout extension point and stays ignorant of who drives it. Also: - The navbar now hosts the in-course state entry with the exit-course action (via a new EditorNavbar slot filled by the editor page). - Exiting a course navigates back to the course list page; the course is kept if the navigation is aborted. - A story video modal (configured via VITE_TUTORIAL_STORY_VIDEO_URL) is shown on the course start page before the course begins; course items on the series page now link to the start page instead of starting directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restarting a course reloads the editor's project state (dropping in-memory edits of the effect-free editing session) and starts the copilot session over. The editor exposes this as a generic reload extension point (editor-reload.ts) driven by the tutorial, keeping the editor ignorant of who requests it. The navbar course entry gains a confirm-guarded "Restart course" menu item. The course start page now accepts a `video` query param to specify the story video per link. Since the param is attacker-controlled, only URLs on the current origin or the usercontent host are accepted; anything else falls back to the configured default. A per-course field on the Course API should replace this once the backend supports it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…able code in chat The course start page now runs an opening sequence: the story video, then an optional text guide the course author embeds in the course prompt as a <course-prelude> section — no backend change needed; the copilot is told the prelude was already shown. The course success element accepts a copilot-written `comment` evaluating the user's actual solution, shown in the success dialog together with a new "Try again" action that restarts the course. Completion guidance now treats goal criteria from the course prompt as the source of truth instead of exact code match. Topics gain a generic `hideCodeInChat` flag (set by tutorial courses): code blocks and code-hint elements keep driving their in-editor guides but no longer display copyable code in the chat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The course copilot gains richer, capability-first ways to guide beyond chat text: - Spotlight now supports a mask mode: everything except the revealed element is dimmed with a soft-edged overlay (never blocking pointer events), with the tip bubble sitting in the dimmed area. The new <spotlight-hint> element triggers it proactively on message arrival (gated to the live round). - <guide-modal> shows must-not-miss guidance in a centered modal, reopenable from a chip in the chat. - <api-video> plays the globally reused explainer video of an API from a library keyed by API definition ID; watched APIs are tracked as learned. - <stay-silent> lets the copilot explicitly do nothing: silent rounds are skipped by the chat UI (falling back to the last meaningful round) and are shown only in developer mode for prompt debugging. The course topic now instructs the copilot to stay silent by default on user events, intervening only when the user is stuck, asks, or needs a genuinely undiscoverable instruction — and to prefer in-editor guides, spotlight, videos and modals over text when it does respond. Also surface sign-in failures (e.g. unregistered redirect URI) as an error toast instead of silently doing nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The context message (custom-element definitions + ambient context + topic instructions) is a single API message, and the backend rejects messages longer than `copilotMessageContentMaxLength` — a tutorial course session (long course prompt, many registered elements, full editor UI info and preloaded skill documents) exceeded it, failing every round with "invalid content: text too long". Truncate the ambient context to the remaining budget, keeping the custom-element definitions and topic instructions intact: they drive the copilot's behavior, while the ambient tail (skill documents etc.) is re-loadable through tools. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e & add step explainer video entry The guide modal is for the one thing the user must not miss, so its content is now raw plain text (no markdown, no nested elements), without a title, and the copilot is instructed to keep it within 30 characters. The API video library gains its first entry (Sprite.step), served from public/tutorial-api-videos/ during development. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…at course start Interventions now follow an escalation ladder: a stuck user first gets one short plain-text hint via the guide modal (direction only, never the answer), then a spotlight / API video, and concrete code guides only after those failed or the user asks for the solution. The guide modal is repositioned accordingly: an intervention tool for stuck users, not a course-opening banner. Course authors declare the new knowledge points of a course in its prompt; the copilot shows the matching explainer videos at the course start, and shows none when no knowledge points are declared. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The course start is now explicitly silent setup: the reply to "Course Started" carries only the invisible setup elements plus declared knowledge-point videos — no greeting, no goal restatement, no proactive pointers to UI locations. The speak-up conditions drop the "next step needs an instruction" loophole: interventions happen only on direct questions, repeated failures (one failure is not enough), or sustained deviation. The example dialogue is rewritten accordingly — the old one demonstrated a chatty welcome with step-by-step highlight links, which taught exactly the proactive behavior the protocol forbids; the new one demonstrates silent setup, staying silent through exploration and first failure, a directional hint on repeated failure, a spotlight only when asked, and success with an evaluation comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The opening reply occasionally carried a sentence narrating the setup
("Let me set up the workspace..."); the protocol now states explicitly
that the reply is the setup elements and nothing else.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uto-open the panel Every user event used to pop the copilot panel open, so collapsing it during a tutorial course (where events are frequent) was futile. Closing the panel by the fold button or by dragging it out of bounds now counts as an explicit collapse: events keep feeding the session so the copilot keeps perceiving, but the panel stays closed until the user opens it again or a new session starts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed API videos
The copilot gains a UI-mode extension point ('floating' | 'docked'); the
tutorial docks it during a course. In docked mode a fixed circular trigger
sits at the bottom-right corner with the panel anchored above it (arrow
tail, resizable height via the dragger, persisted); clicking the trigger
or anywhere outside dismisses the panel as an explicit collapse. The
focused-layout run controls move beside the trigger, matching the
game-window design.
API explainer videos no longer render a card in the chat: the element
opens the video modal directly when its (live) message arrives, and the
copilot re-emits it on request. The course protocol now also forbids
re-emitting setup elements on later events — re-emitted videos would pop
over whatever the user is doing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… is on screen During a course the copilot no longer only waits for user input: while it has no visible presence — no guidance modal / API video / in-editor code guide (tracked via a new visible-artifact registry on Copilot) and no visible reply in the open panel — an "Auto perception" event is sent every 5 seconds so it can observe the latest editor state. Perception pauses while the page is hidden, while a round is pending, and after 4 consecutive silent perceptions (the protocol asks for a hint on the 3rd; the cap guards against disobedience); any other user event resets it. The course protocol distinguishes the sources: auto perceptions are not user requests and follow the strict silence rules, while the "Next step" quick input is an explicit ask deserving an immediate (still restrained) response. Auto perception events do not auto-open the panel (notifyUserEvent gains an autoOpen option). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…thing on screen The "visible reply in the open panel" check treated any non-empty round content as visible, but a reply consisting of render-null elements only (the course setup: workspace hiding, API narrowing, api-video...) shows nothing — with the panel open it blocked auto perception forever right after the course start. Strip all known invisible element tags when judging whether a round produced user-visible output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…k API reference items The copilot occasionally re-emits <api-video> despite instructions (e.g. on auto-perception rounds), popping the video over the user repeatedly. Auto-play now happens at most once per API per session; re-emissions render a small chip the user clicks to (re)play, and the element description tells the copilot so. In the focused layout the API reference items render as draggable blocks — card surface with a grip-dots handle and grab cursor — so dragging them into the code editor reads as the obvious interaction. Wired as a pass-through prop (editor → CodeEditorUI → APIReferenceUI), keeping xgo-code-editor ignorant of the workspace layout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The silence protocol over-generalized: a plain greeting typed by the user is not a question, so the copilot classified it as an event needing no reaction and stayed silent — which reads as "the copilot ignores me", and breaks courses whose goal is interacting with the copilot itself. The protocol now distinguishes the two by their wire format: silence applies only to <event>-wrapped messages; anything the user typed always gets a response. Course-specific completion criteria (possibly non-coding, e.g. "send the copilot a message") explicitly take precedence over every generic rule, including silence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… leaked reasoning around it
The model occasionally narrates its inner reasoning before the stay-silent
element ("Let me check the current state... <stay-silent />"); the silence
filter required the element to be the entire content, so the leaked English
monologue was shown to the user. The protocol requires stay-silent to stand
alone — when it appears at all, whatever surrounds it was never meant for
the user, so its mere presence now counts as silence (in both the chat
filter and auto-perception counting).
The course protocol additionally forbids writing reasoning into any reply
and requires user-facing text in the user's language.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The label lives inside the stage container, so moving across it bubbled a mousemove up to the stage's hover tracking, which hit-tested that point, found no sprite under the label, and started hiding the thing the pointer was sitting on. Entering it cancelled the countdown once; the next move restarted it. The label swallows its own mousemove now, and starts the countdown when the pointer actually leaves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The label still vanished now and then once the pointer reached it. Swallowing its own mousemove was not enough: the stage's hover tracking is throttled, so a call scheduled while the pointer was still crossing the gap could run after it had arrived, hit-test that stale point, find no sprite, and start hiding the label the pointer was resting on — and with the label swallowing events, nothing was left to cancel the countdown. Whether that trailing call landed before or after the pointer arrived is what made it intermittent. The pointer being on a label is now a fact the hide path checks, rather than something inferred from whichever event happens to arrive last. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This reverts commit 3a52364.
wenjiangping
force-pushed
the
issue-3412-B
branch
from
August 21, 2026 08:47
daeb1f8 to
a2bc8c6
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related
Related to #3425
Background
The multi-sprite tutorial editor should preserve the standard editor layout used by the dev environment while providing the same ruler interaction available in the single-sprite tutorial mode. The existing course-filtered API list also needs to remain understandable by keeping its visible categories aligned with the code items used by the current course.
UX Changes
Scope
Verification