feat(ui): rebuild Start a session on subturtle-ui - #68
Open
SomiVista wants to merge 2 commits into
Open
Conversation
Rebuilds /sessions/new — the page, the Gemini StartNew entry point, the shared StartLiveSessionForm and VoicePicker — in the st- design language. All script logic is carried over verbatim: the dataProvider bundle controller, GEMINI_VOICES, the formData shape, isFormValid over the form's exposed selectionError/randomError, the freemium and voice-cap gates, and pickPhraseIds + encodeSessionRequest. Layout follows the design: a two-column grid with the bundle picker and coach card on the left and a sticky summary panel on the right, carrying the selection, the voice-minute or freemium meter, and the single Start button. Adds StSegmentedControl and StProgressBar to subturtle-ui, ported from the design system's SegmentedControl.jsx and ProgressBar.jsx. Both are real design-system primitives and both are used again by Session history. Three design elements are deliberately not shipped, for want of data: - The "Due today" / "Never practised" bundle chips. Leitner due-counts live per phrase in a separate database and a bundle carries no last-practised marker, so only Recent / All are wired. - "Repeat your last session". Its only source is the list-live-sessions RPC, which is Learner+ gated and throws for the free tier — not cheap, and broken for the tier most likely to be on this page. - The voice "Preview" pill. CoachVoice reserves avatarUrl as null and carries no audio; there is no preview endpoint. The bundle pick tile, coach voice tile and filter chip stay page-local: they are single-screen specialisations with no design-system counterpart, following the call already made for InlineNotice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebuilds /sessions/new to the redesign: a two-column layout with the bundle picker and coach card on the left and a sticky session summary on the right. Session logic is unchanged — the same dataProvider bundle controller, form shape and defaults, selectionError/randomError validation, freemium gate and pickPhraseIds + encodeSessionRequest hand-off to /practice/live-session. Library: - StSegmentedControl and StProgressBar, ported from the design system's SegmentedControl.jsx / ProgressBar.jsx, plus seven Solar icons. Screen: - Bundle picker with search, Recent / Due today / Never practised / All chips, and a per-bundle "N phrases · N due" line on a colour-coded tile. - Coach cards carry the server voice list's descriptions and a Preview pill that plays a sample through the existing textToSpeechBase64 function. - Summary panel: bundle, mode, voice, fallback, phrase range, the voice-minute meter and the primary Start session button. - "Repeat your last session" replays the last setup from localStorage rather than from list-live-sessions, which is Learner+ and throws for the free tier. The picker's due counts need review state the client cannot read (leitner_system is owner-access), so get-phrase-management-info returns an additional duePhraseIds array. The field is additive; its only existing caller ignores it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Rebuilds Start a session (
/sessions/new) on subturtle-ui, matching the design in the redesign project.Second-to-last screen in the pilotui → subturtle-ui migration. Session logic is unchanged: the same
dataProviderbundle controller,formDatashape and defaults,isFormValidvia the exposedselectionError/randomError, the freemiumopenVoiceCapModalgate, andpickPhraseIds+encodeSessionRequest→/practice/live-session.Library (
ui/)StSegmentedControlandStProgressBar, ported from the design system'scomponents/core/SegmentedControl.jsxandProgressBar.jsx. Two deliberate additions the React sources can't express with inline styles::focus-visiblerings, andblockon the segmented control so it can stretch to a form field.ICONSallowlist.Screen
N phrases · N due.The bundle tile colour is derived from the bundle id (djb2), so it is stable for the bundle's life and identical in the picker and the summary, with no schema change.
Two things that needed data, not just markup
Due counts. The picker's
N dueline and the Due today / Never practised chips need review state, andleitner_systemis an owner-access collection the client cannot read.get-phrase-management-infonow also returnsduePhraseIds. The field is additive and its one existing caller (the Leitner phrase picker) ignores it.Repeat your last session. Sourced from
localStorage, written when a session starts — not fromlist-live-sessions, which is a Learner+ entitlement that throws for exactly the tier most likely to press the button. It fills the form in rather than starting immediately, so the setup stays visible before any voice minutes are spent. The trade-off is that it is per-browser.Voice previews — needs a decision
The Preview pill plays a sample through the existing
textToSpeechBase64function, mapping each Gemini coach to the closest Google TTS voice. It conveys the tone the description promises but is not the exact voice the session will use — the Gemini Live prebuilt voices aren't addressable outside a live session. If that mismatch isn't acceptable, say so and I'll drop the pill;useVoicePreview.tsis self-contained and the mapping is one constant.Verification
yarn test:unit— 53 passing, 10 new covering the due-count and never-practised logic inutils/bundleReviewState.ts.duePhraseIdsconfirmed over the wire against a scratch server on :8091 (6 due across 2 seeded bundles).Server tests were not run to completion:
yarn testOOMs in Jest. I confirmed this is pre-existing — it reproduces on a clean tree with this change stashed, on both node 18 and 22.pilotui stays installed and untouched elsewhere; the shared
StartLiveSessionFormkeeps its props,v-modelanddefineExposecontract, so the bundle-detail modal and the legacy OpenAI entry point still work.🤖 Generated with Claude Code