Repository navigation
iOS parity round 3: voice rounds, the free round timer with a Live Activity, and practice tools - #262
Conversation
… core The app's timer, speech-time calculator and motion generator need the same logic as the website's free tools. Each old src/lib path re-exports the core module; the custom format's URL codec stays in the website, and core gains parseCustomFormat for formats kept on a device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Timer now runs the free timer's formats (BP, WSDC, AP, PF, LD, Policy and the school format), each speech's points-of-information window with the knock, prep banks for both sides, overtime and grace, and custom formats saved on the device. The round is kept in AsyncStorage and its signals run from the root layout, so closing the timer leaves a speech running. With an App Group (DEBATELAB_APP_GROUP) the development build also shows the clock as a Live Activity in the Dynamic Island and on the Lock Screen, and as a lock-screen widget, drawn by iOS from SwiftUI timer texts. Expo Go and builds without the group skip all of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sparring and Crossfire get the website's voice rounds: the student speaks each turn through Apple's speech recognition (expo-speech-recognition, restarting when iOS ends a request, as the website's capture does), the words fill the draft, and the turn's speaking time, pace, fillers and long pauses are measured with the shared delivery code. The opponent's replies play from the voice function's signed ElevenLabs segments with expo-audio, falling back to the iOS voice. The first-use privacy notice keeps the website's wording, with Apple in place of the browser. Generate's motion and Ask's question take dictation. Voice needs the development build; in Expo Go the switch stays off with the existing note and the dictation buttons do not show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scorecard's menu exports the website's Sparring PDF (set-up, totals, category scores and feedback, key moments, the transcript) through expo-print, with each spoken speech's measured delivery. Word export is left out: the docx package could not be shown to run in Hermes here. A saved Crossfire round now opens from History and the Debate tab: the debrief, the spoken delivery of a voice round and the transcript with its signals, read from the crossfires row the website saves, with an offline copy like every other read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both are the website's free tools, from the same core logic and the website's tools catalog: the calculator counts a speech's words, times it at four paces and shows which slots of the seven formats it fits; the generator filters the practice motion bank by topic, level and type and draws one motion or three. A drawn motion can be copied, shared or opened as a new debate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wide iPad's outline rail is a menu too, so the first menu item was the rail's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Words heard after a Sparring Reset no longer start the reset turn's clock: dictated words append to the draft without the start path. - One capture uses iOS's recognizer at a time; starting another cancels the first, so two screens never end or hear each other's sessions. - An opponent clip that never reports its end gives up after its length plus slack, so the device voice reads the rest; clips fetched for a reply that stopped are deleted. - Sending a spoken turn with nothing heard returns quietly. - Generate's dictation keeps words that arrive between renders. - A custom format saved before the stored list loads no longer replaces it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
…oject The app is com.pistack.debateowl, its widget extension com.pistack.debateowl.ExpoWidgetsTarget, and both carry the App Group group.com.pistack.debateowl, the only value DEBATELAB_APP_GROUP accepts. The config links the EAS project @pi-stack/debateowl that eas init created, and eas.json gains a development-simulator profile: an unsigned development client for the iOS Simulator, with the widget extension. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EAS build b8e4ba30 failed in the Xcode phase that bundles the widget runtime: expo-widgets' metro.config.js makes the package its project root, and Expo CLI's resolver then cannot reach Metro's empty module in the workspace root's node_modules. An eas-build-post-install hook gives that bundle an empty module inside the package; it fails loudly if a later expo-widgets changes the config it edits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…colours
The live round passed no formatted number, so it read {{formatted}} words. The lock-screen widget now uses the timer screen colours for side B and both sides.
|
Replaced by #266, which combined rounds 3 and 4 and is now merged. |
Round 3 of iOS parity (plan:
PARITY.md, section "Round 3"). It brings the website's practice tools to the app: voice rounds and dictation, the free round timer with a Lock Screen clock, the Sparring scorecard as a PDF, saved Crossfire rounds, and the speech time calculator and motion generator.What changed
Voice rounds (Sparring and Crossfire). The student speaks each turn through Apple's speech recognition (
expo-speech-recognition). The words fill the draft and can be corrected. On submit, the turn's speaking time, words per minute, fillers and long pauses are measured with the website's shared delivery code.speech-capture.tsports the website's capture hook: it restarts when iOS ends a request, gives up after three quick failures, and waits 1.5 s on stop for the last words. Turns sendvoice: trueand play the opponent's signed ElevenLabs segments from the existingvoicefunction with expo-audio, falling back to the iOS voice. The first-use privacy notice keeps the website's wording, with Apple's recognizer in place of the browser. The scorecard and the Crossfire debrief show the measured delivery.Dictation. A microphone beside Generate's motion and in Ask's composer.
Timer. It is now the website's free round timer, from
packages/core/src/free-tools, which moved there fromsrc/lib/free-toolswith re-exports, so the website's imports are unchanged. It has seven formats (BP, WSDC, AP, PF, LD, Policy, school), POI windows with the knock, prep banks for both sides, overtime and grace, and custom formats saved on the device. The round persists, and its signals run from the root layout, so a speech keeps running after the timer closes. With an App Group, the development build also shows the clock as a Live Activity (Lock Screen and Dynamic Island) and a lock-screen widget. iOS draws both from SwiftUI timer texts, so they count without the app. This resolvesTODO(live-activity).Sparring PDF. The scorecard's menu exports the website's PDF, plus each spoken speech's measured delivery.
Saved Crossfire rounds open read only from History and the Debate tab, with an offline copy.
Practice gains the speech time calculator and the motion generator. "Prep both sides" opens New debate with the motion filled in.
Expo Go
Expo Go keeps working. The voice switch stays off with the existing "Coming with the app build" note, dictation buttons do not show, and nothing loads
expo-speech-recognition,expo-widgetsor@expo/uiat a file's top level. The config withoutDEBATELAB_BUILDis unchanged:npx expo config --jsonis identical before and after.expo-speechand@expo/ui(bumped to 57.0.26 to dedupe the copy expo-widgets brings) are in the SDK's module list.Checks
npm run verifyat the final head: exit 0 (docs, lint and knip, typecheck, 814 web tests, 30 collaboration tests, edge tests, build, service worker, 47 Chromium smoke tests). This is a local pass. GitHub Actions is billing-blocked, so its failures here are expected and say nothing about the code.parseCustomFormat.expo export --platform iosbuilds the Hermes bundle. Babel's'widget'transform serializes both widget layouts.DEBATELAB_BUILD=dev DEBATELAB_APP_GROUP=group.com.adam.debatelab expo prebuild --platform ios --no-installon Linux generates:ExpoWidgetsTargetextension withDebateTimerWidgetandWidgetLiveActivity;NSSupportsLiveActivities;aps-environment;InfoPlist.strings.scripts/screenshots.ts, with newvoiceandtoolsgroups:Bundle id, App Group and EAS
com.pistack.debateowl, and its widget extension iscom.pistack.debateowl.ExpoWidgetsTarget. Both carry the App Groupgroup.com.pistack.debateowl, which is the only valueDEBATELAB_APP_GROUPaccepts.eas initcreated@pi-stack/debateowl(projectd60fc378-2e30-48b2-bcee-a1b1d1632bf9). The config hasowner: 'pi-stack'and that project id.eas.jsonhas a newdevelopment-simulatorprofile: an unsigned development client for the iOS Simulator, with the widget extension.metro.config.jsmakes the package its project root, and Expo CLI's resolver then cannot reach Metro's empty module in the workspace root'snode_modules. I reproduced this on Linux. Aneas-build-post-installhook (apps/mobile/scripts/patch-expo-widgets-bundle.mjs) gives that bundle an empty module inside the package. It fails loudly if a later expo-widgets changes the config it edits; expo-widgets 57.0.27 ships the same config.DebateOwl.app(com.pistack.debateowl, Live Activities on, the App Group, Hungarian and Spanish permission strings),PlugIns/ExpoWidgetsTarget.appexand the widget bundle.com.adam.debatelabsurvives only undersupabase/, as in-app-purchase product ids (com.adam.debatelab.pro.monthly) and a test bundle constant. Those are App Store product ids and backend code, so this PR leaves them.What still needs Adam
group.com.pistack.debateowlfor bothcom.pistack.debateowlandcom.pistack.debateowl.ExpoWidgetsTarget, with Live Activities enabled.developmentprofile needsDEBATELAB_APP_GROUP=group.com.pistack.debateowlin itsenvonce the provisioning profile carries the group, followingdocs/development.md#build-day.Not in this PR
docxpackage runs in Hermes: there is no Hermes runtime on this VPS. PDF only.supabase/, no migrations, no deploys. Thevoicefunction is used as it is.widgets[]array inapp.config.ts. Expect a small merge there.🤖 Generated with Claude Code