Follow-up to #132. The SQL playground theme picker (SqlThemePickerComponent) writes the new theme to localStorage and exposes a themeChangedSignal, but nothing in the shell reacts to that signal yet. The user sees the dropdown change and assumes the world changed; in practice:
The lesson-pane instructions still describe the OLD theme's tables (travelers / home_country / etc.)
The output pane still shows the result of the last query against the old schema
No toast or status hint tells the student "OK, your next Submit will run against Animals"
The lesson LessonAttempt.instructions / hints / codeTemplate are baked in at attempt-load time from the default theme — the SPA needs to re-pick from the per-theme bindings in the cached SqlLessonPlan when the theme changes
This is purely a polish ticket on top of the working frontend; the grading logic is already theme-aware.
What needs to happen
1. Re-render lesson content on theme change
In LanguageRunner (or wrap inside SqlRunner): when the active SQL theme changes, re-derive the lesson pane's instructions, hints, and starterQuery from the cached SqlLessonPlan.themes[activeTheme].
The cleanest seam: expose a hook on the runtime adapter — ILanguageRuntimeAdapter.refreshAttemptDisplay(lessonNumber) or similar — that the shell calls when the picker emits. Default implementation is a no-op; SQL overrides to pick the binding and surface its strings.
Alternative seam without touching the cross-language adapter: have SqlRunner listen to themeChangedSignal and call runtime.getAttempt(lessonNumber) again (the offline cache will return the same plan, the SqlRunnerService just re-projects the active theme).
2. Toast / inline hint on switch
When the theme changes mid-attempt, surface a one-line confirmation — either:
An Angular MatSnackBar toast: "Theme switched to Animals — your next Submit will grade against researchers and sightings."
Or an inline .sql-theme-hint line under the picker bar that fades in for a few seconds
The hint copy must mention the new theme's main table so the student immediately knows what to rename in their query.
3. Clear stale output / last-result
Call runtime.clearResult() when the theme changes so the previously-rendered "32 rows returned" doesn't mislead the student into thinking it applies to the new theme. Reset is sufficient — next Run/Submit repopulates the pane.
4. Optionally: warn before switch if pending changes
If the editor contains content that doesn't match the active theme's starter query (i.e. the student has typed real work), prompt before swapping: "Switch theme? Your current draft will stay in the editor, but the column names won't match." Low priority — the editor content is theirs anyway and forcibly clearing it would be worse.
5. Persist theme across lesson navigation
Confirm the picker's localStorage value survives a hard reload AND tab close, and that opening a different lesson keeps the choice intact. Today the localStorage write happens in SqlThemePickerComponent.onChange — verify in a spec.
Acceptance
Switching theme during a lesson updates the visible instructions / hints / starter without a page reload
A toast or inline hint confirms the switch and names the new theme's primary table
The output pane clears so old result rows don't mix with a new query
localStorage value survives reload + tab close (spec-covered)
No regression in non-SQL playgrounds (the new adapter hook, if any, defaults to no-op)
Labels
enhancement · frontend · ux · sql
## Context
Follow-up to [#132](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/132). The SQL playground theme picker (`SqlThemePickerComponent`) writes the new theme to localStorage and exposes a `themeChangedSignal`, but **nothing in the shell reacts to that signal yet**. The user sees the dropdown change and assumes the world changed; in practice:
- The lesson-pane instructions still describe the OLD theme's tables (`travelers` / `home_country` / etc.)
- The output pane still shows the result of the last query against the old schema
- No toast or status hint tells the student "OK, your next Submit will run against Animals"
- The lesson `LessonAttempt.instructions` / `hints` / `codeTemplate` are baked in at attempt-load time from the default theme — the SPA needs to re-pick from the per-theme bindings in the cached `SqlLessonPlan` when the theme changes
This is purely a polish ticket on top of the working frontend; the grading logic is already theme-aware.
## What needs to happen
### 1. Re-render lesson content on theme change
In `LanguageRunner` (or wrap inside `SqlRunner`): when the active SQL theme changes, re-derive the lesson pane's `instructions`, `hints`, and `starterQuery` from the cached `SqlLessonPlan.themes[activeTheme]`.
The cleanest seam: expose a hook on the runtime adapter — `ILanguageRuntimeAdapter.refreshAttemptDisplay(lessonNumber)` or similar — that the shell calls when the picker emits. Default implementation is a no-op; SQL overrides to pick the binding and surface its strings.
Alternative seam without touching the cross-language adapter: have `SqlRunner` listen to `themeChangedSignal` and call `runtime.getAttempt(lessonNumber)` again (the offline cache will return the same plan, the SqlRunnerService just re-projects the active theme).
### 2. Toast / inline hint on switch
When the theme changes mid-attempt, surface a one-line confirmation — either:
- An Angular `MatSnackBar` toast: "Theme switched to Animals — your next Submit will grade against `researchers` and `sightings`."
- Or an inline `.sql-theme-hint` line under the picker bar that fades in for a few seconds
The hint copy must mention the **new theme's main table** so the student immediately knows what to rename in their query.
### 3. Clear stale output / last-result
Call `runtime.clearResult()` when the theme changes so the previously-rendered "32 rows returned" doesn't mislead the student into thinking it applies to the new theme. Reset is sufficient — next Run/Submit repopulates the pane.
### 4. Optionally: warn before switch if pending changes
If the editor contains content that doesn't match the active theme's starter query (i.e. the student has typed real work), prompt before swapping: "Switch theme? Your current draft will stay in the editor, but the column names won't match." Low priority — the editor content is theirs anyway and forcibly clearing it would be worse.
### 5. Persist theme across lesson navigation
Confirm the picker's localStorage value survives a hard reload AND tab close, and that opening a different lesson keeps the choice intact. Today the localStorage write happens in `SqlThemePickerComponent.onChange` — verify in a spec.
## Acceptance
- [ ] Switching theme during a lesson updates the visible instructions / hints / starter without a page reload
- [ ] A toast or inline hint confirms the switch and names the new theme's primary table
- [ ] The output pane clears so old result rows don't mix with a new query
- [ ] localStorage value survives reload + tab close (spec-covered)
- [ ] No regression in non-SQL playgrounds (the new adapter hook, if any, defaults to no-op)
## Labels
`enhancement` · `frontend` · `ux` · `sql`
Toast feedback: SqlRunner shell now subscribes to the theme picker's themeChangedSignal() via Angular effect() and surfaces a MatSnackBar toast on real changes (skipping the first effect run which only reflects the persisted preference). The toast names the new theme's primary table so the student immediately knows which table to query — e.g. "Theme switched to Animals — your next Run grades against researchers and friends."
Stale-output clearing: the same effect calls runner.clearResult() on theme change so the previously-rendered result rows don't mislead the student into thinking they apply to the new theme.
Lesson display projection: SqlRunnerService.getAttempt() now post-processes the returned LessonAttempt to overlay the active theme's instructions, hints, and starterQuery from the cached SqlLessonPlan. The backend bakes Travel-default strings into the cross-language contract; this client-side projection means the lesson pane shows the right theme's column names for the student's selected world. Tutorial-kind lessons (Chapter 0) pass through unchanged — they have no plan.
localStorage persistence: new sql-theme.types.spec.ts pins the round-trip contract: read returns default when nothing is stored, round-trips each valid theme, falls back to default on garbage values.
Verification: 34 frontend tests pass (25 hint patterns + 9 theme types), full spikersoft suite still green (1755/1755).
Remaining gap: switching theme mid-lesson while the lesson pane is open doesn't yet auto-re-fetch the attempt — the projection happens on getAttempt, so the student has to navigate away and back to see the new theme's instructions. The shell could expose a refreshAttemptDisplay(lessonNumber) hook on ILanguageRuntimeAdapter for live re-renders, but that's a small follow-up and the current behavior (clear output + toast + new theme on next Submit) covers the core UX.
Closing.
## Closed by implementation
Shipped:
- **Toast feedback**: `SqlRunner` shell now subscribes to the theme picker's `themeChangedSignal()` via Angular `effect()` and surfaces a `MatSnackBar` toast on real changes (skipping the first effect run which only reflects the persisted preference). The toast names the new theme's primary table so the student immediately knows which table to query — e.g. "Theme switched to Animals — your next Run grades against `researchers` and friends."
- **Stale-output clearing**: the same effect calls `runner.clearResult()` on theme change so the previously-rendered result rows don't mislead the student into thinking they apply to the new theme.
- **Lesson display projection**: `SqlRunnerService.getAttempt()` now post-processes the returned `LessonAttempt` to overlay the active theme's `instructions`, `hints`, and `starterQuery` from the cached `SqlLessonPlan`. The backend bakes Travel-default strings into the cross-language contract; this client-side projection means the lesson pane shows the right theme's column names for the student's selected world. Tutorial-kind lessons (Chapter 0) pass through unchanged — they have no plan.
- **localStorage persistence**: new [`sql-theme.types.spec.ts`](spikersoft-angular/libraries/features/dev-tools-sql-runner/src/lib/sql-theme.types.spec.ts) pins the round-trip contract: read returns default when nothing is stored, round-trips each valid theme, falls back to default on garbage values.
**Verification**: 34 frontend tests pass (25 hint patterns + 9 theme types), full spikersoft suite still green (1755/1755).
**Remaining gap**: switching theme mid-lesson while the lesson pane is open doesn't yet auto-re-fetch the attempt — the projection happens on `getAttempt`, so the student has to navigate away and back to see the new theme's instructions. The shell could expose a `refreshAttemptDisplay(lessonNumber)` hook on `ILanguageRuntimeAdapter` for live re-renders, but that's a small follow-up and the current behavior (clear output + toast + new theme on next Submit) covers the core UX.
Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Context
Follow-up to #132. The SQL playground theme picker (
SqlThemePickerComponent) writes the new theme to localStorage and exposes athemeChangedSignal, but nothing in the shell reacts to that signal yet. The user sees the dropdown change and assumes the world changed; in practice:travelers/home_country/ etc.)LessonAttempt.instructions/hints/codeTemplateare baked in at attempt-load time from the default theme — the SPA needs to re-pick from the per-theme bindings in the cachedSqlLessonPlanwhen the theme changesThis is purely a polish ticket on top of the working frontend; the grading logic is already theme-aware.
What needs to happen
1. Re-render lesson content on theme change
In
LanguageRunner(or wrap insideSqlRunner): when the active SQL theme changes, re-derive the lesson pane'sinstructions,hints, andstarterQueryfrom the cachedSqlLessonPlan.themes[activeTheme].The cleanest seam: expose a hook on the runtime adapter —
ILanguageRuntimeAdapter.refreshAttemptDisplay(lessonNumber)or similar — that the shell calls when the picker emits. Default implementation is a no-op; SQL overrides to pick the binding and surface its strings.Alternative seam without touching the cross-language adapter: have
SqlRunnerlisten tothemeChangedSignaland callruntime.getAttempt(lessonNumber)again (the offline cache will return the same plan, the SqlRunnerService just re-projects the active theme).2. Toast / inline hint on switch
When the theme changes mid-attempt, surface a one-line confirmation — either:
MatSnackBartoast: "Theme switched to Animals — your next Submit will grade againstresearchersandsightings.".sql-theme-hintline under the picker bar that fades in for a few secondsThe hint copy must mention the new theme's main table so the student immediately knows what to rename in their query.
3. Clear stale output / last-result
Call
runtime.clearResult()when the theme changes so the previously-rendered "32 rows returned" doesn't mislead the student into thinking it applies to the new theme. Reset is sufficient — next Run/Submit repopulates the pane.4. Optionally: warn before switch if pending changes
If the editor contains content that doesn't match the active theme's starter query (i.e. the student has typed real work), prompt before swapping: "Switch theme? Your current draft will stay in the editor, but the column names won't match." Low priority — the editor content is theirs anyway and forcibly clearing it would be worse.
5. Persist theme across lesson navigation
Confirm the picker's localStorage value survives a hard reload AND tab close, and that opening a different lesson keeps the choice intact. Today the localStorage write happens in
SqlThemePickerComponent.onChange— verify in a spec.Acceptance
Labels
enhancement·frontend·ux·sqlClosed by implementation
Shipped:
SqlRunnershell now subscribes to the theme picker'sthemeChangedSignal()via Angulareffect()and surfaces aMatSnackBartoast on real changes (skipping the first effect run which only reflects the persisted preference). The toast names the new theme's primary table so the student immediately knows which table to query — e.g. "Theme switched to Animals — your next Run grades againstresearchersand friends."runner.clearResult()on theme change so the previously-rendered result rows don't mislead the student into thinking they apply to the new theme.SqlRunnerService.getAttempt()now post-processes the returnedLessonAttemptto overlay the active theme'sinstructions,hints, andstarterQueryfrom the cachedSqlLessonPlan. The backend bakes Travel-default strings into the cross-language contract; this client-side projection means the lesson pane shows the right theme's column names for the student's selected world. Tutorial-kind lessons (Chapter 0) pass through unchanged — they have no plan.sql-theme.types.spec.tspins the round-trip contract: read returns default when nothing is stored, round-trips each valid theme, falls back to default on garbage values.Verification: 34 frontend tests pass (25 hint patterns + 9 theme types), full spikersoft suite still green (1755/1755).
Remaining gap: switching theme mid-lesson while the lesson pane is open doesn't yet auto-re-fetch the attempt — the projection happens on
getAttempt, so the student has to navigate away and back to see the new theme's instructions. The shell could expose arefreshAttemptDisplay(lessonNumber)hook onILanguageRuntimeAdapterfor live re-renders, but that's a small follow-up and the current behavior (clear output + toast + new theme on next Submit) covers the core UX.Closing.