Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a247421
Share the Library's file reader, sort orders and folder list with the…
Programcode Oct 28, 2026
ceaf1b6
Add one haptics helper and an app toast, and tap the shared controls
Programcode Oct 28, 2026
984c9df
Give the app the website's Library: files, folders, the Active switch…
Programcode Oct 28, 2026
6c20bb7
Press and hold saved work for a preview and its menu
Programcode Oct 28, 2026
0b6be85
Debates: role, judge and instructions, Edit settings, Delete and the …
Programcode Oct 28, 2026
6183055
Test the shared file reader's limits, truncation, JSON fallback and P…
Programcode Oct 28, 2026
de1c315
Shoot the Library, file import and debate screens from fixed rows, an…
Programcode Oct 28, 2026
cd9774a
Flatten a linked row's style for the web, and close a source opened w…
Programcode Oct 28, 2026
c78db6c
Keep Hungarian source text whole beside an open source on iPad
Programcode Oct 28, 2026
a689f67
Shoot the end of a long sheet with a window tall enough for it
Programcode Oct 28, 2026
0e4b0fb
Keep a new or renamed folder chosen, and keep the layout's diff small
Programcode Oct 28, 2026
029d731
Open Crossfire and chat rows again on the Debate tab and Prepare
Programcode Oct 28, 2026
6d906cd
Read a picked file in slices, after a real size check, and never hang
Programcode Oct 28, 2026
7de2b13
Let the newest switch change decide, and keep the switch out of the r…
Programcode Oct 28, 2026
a540b1b
Website: keep the app's judge when Edit settings saves, and match leg…
Programcode Oct 28, 2026
a6c5e50
Describe the sliced file read in the app's guide
Programcode Oct 28, 2026
e6591ca
Merge remote-tracking branch 'origin/feat/native-ios' into native-ios…
Programcode Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions apps/mobile/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ The iOS app DebateOwl (`@debatelab/mobile`; the bundle id is still `com.adam.deb

## Layout

- `src/app/`: routes. `sign-in.tsx`; `(tabs)/` with one route group per tab: Debate, Prepare, Practice, Library, You; the form sheets `debates.tsx` (switcher), `new-debate.tsx`, `delete-account.tsx`, `delete-data.tsx`, `add-link.tsx`, `new-note.tsx`, `source/[id].tsx`, `limit.tsx` (the website's limit-reached and model-restricted dialogs), `save-case.tsx` (Save case with a title) and `context-review.tsx` (the debate context Review); and `ask.tsx`, `generate.tsx`, `case/[id].tsx`, `attack/`, `brainstorm/`, `factoids/` and `sparring/` (each a form `index.tsx` and a document `[id].tsx`), `crossfire.tsx` and `timer.tsx` (a full-screen modal), `flowchart/` (the screen `index.tsx` with its `card`, `claim` and `maps` form sheets), and `history.tsx`, full screen above the tabs. All are declared in the root `_layout.tsx`. Every Practice tool is in the app, so `(practice)/soon/[tool].tsx` is no longer reached from the Practice tab.
- `src/app/`: routes. `sign-in.tsx`; `(tabs)/` with one route group per tab: Debate, Prepare, Practice, Library, You; the form sheets `debates.tsx` (switcher), `new-debate.tsx`, `delete-account.tsx`, `delete-data.tsx`, `add-link.tsx`, `new-note.tsx`, `add-file.tsx` (Import file), `source/[id].tsx`, `edit-debate.tsx` (Edit settings), `debate-check.tsx` (the five-day check), `limit.tsx` (the website's limit-reached and model-restricted dialogs), `save-case.tsx` (Save case with a title) and `context-review.tsx` (the debate context Review); and `ask.tsx`, `generate.tsx`, `case/[id].tsx`, `attack/`, `brainstorm/`, `factoids/` and `sparring/` (each a form `index.tsx` and a document `[id].tsx`), `crossfire.tsx` and `timer.tsx` (a full-screen modal), `flowchart/` (the screen `index.tsx` with its `card`, `claim` and `maps` form sheets), and `history.tsx`, full screen above the tabs. All are declared in the root `_layout.tsx`. Every Practice tool is in the app, so `(practice)/soon/[tool].tsx` is no longer reached from the Practice tab.
- `src/components/`: shared UI. `app-tabs.tsx` uses native tabs; `app-tabs.web.tsx` draws the mock's tab bar for the web preview.
- `src/lib/`: auth (`auth.tsx`), `native-capabilities.ts` (what the running binary has beyond Expo Go), the Supabase client, session storage (AES key in SecureStore, ciphertext in AsyncStorage) and i18n. `debates.ts`, `settings.tsx`, `subscription.ts` and `account.ts` read and write the website's tables the way its stores do (`src/lib/stores/debate-store.ts`, `settings-store.ts`, `src/hooks/use-subscription.ts`, `src/lib/account.ts`), so the app and the website share one Current Debate and one set of settings. Change them together with the website code they mirror.
- `sources.ts` mirrors the session store's `addSource` and `removeSource` and `src/lib/source-import.ts`. The Library always shows the Current Debate, also in one-off mode, as on the website. Adding a file (the website parses files in the browser) and Scan pages are not in the app yet.
- `sources.ts` mirrors the session store's source actions (`addSource` with Undo's restore, `updateSource`, `removeSource`, `toggleSource` and `setFolderActive` as `setSourcesActive`, the folder actions and `clearLibrary`) and `src/lib/source-import.ts`. Switch changes run in one serial queue, so they reach the server in the order the student made them; a screen shows a change at once and puts it back when the write fails (`source-actions.ts`, with the website's messages). The Library always shows the Current Debate, also in one-off mode, as on the website. Its folder list, search and eight sort orders are the website's, in `packages/core/src/library.ts`. Scan pages is not in the app yet.
- File import runs the website's reader (`packages/core/src/source-file-import.ts`, with its 8 MB and 120,000-character limits) inside an Expo DOM component, `components/source-file-reader.tsx`, because pdf.js and mammoth need a browser. `pick-source-file.ts` opens the Files picker, `source-file.tsx` checks size (the file's own when the picker gives none) and type before reading anything, then serves the picked copy in 768 KB slices that the reader pulls one at a time (`picked-file.ts` reads them natively as base64; the web preview slices the browser File); a read always ends, with a two-minute timeout for a reader that never loads, and `add-file.tsx` shows the draft. The DOM component imports pdf.js and mammoth statically, because Expo cannot export a DOM component that splits into chunks; its webview loads only while a file is read. It uses `@expo/dom-webview`, which Expo Go has. Never move the parsing into `import-source`: that would be an Edge Function deploy.
- `toast.tsx` is the app's toast (the website's short messages, with Undo); a form sheet is drawn above it, so a sheet says its outcome inline and the toast shows once the sheet closes. `haptics.ts` is the one place for haptics (a tap for switches and chips, a tick for segmented controls and pickers, success when a result arrives or something is saved, a warning when a request fails, a tap when the turn changes); `haptics.web.ts` records them for the screenshot script.
- Debates: `new-debate.tsx` and `edit-debate.tsx` share `components/debate-form.tsx`, the website's DebateForm plus the judge (`settings.judge`, which the website's prompts read but its form does not show). The switcher deletes any debate but the current one (the database cascades its sources and work). `use-inactive-debate-check.ts` opens `debate-check.tsx`, the website's five-day check; closing it counts as Continue.
- Rows of saved work and sources are `Link`s with `Link.Preview` and `Link.Menu`: a long press shows the document and its actions (`work-actions.ts`: Discuss, Share, and Delete where the website's History deletes).
- Ask mirrors `src/lib/ai/streaming.ts` and `askChat` in `src/lib/ai/client.ts`: `ask.ts` sends the requests (plain questions stream from `ai-stream` through `expo/fetch`; with Citations, Web search, Pros/Cons or Argument map on, the whole answer comes from `ai`, because `ai-stream` refuses those options), `ai-request.ts` holds the shared POST with the website's headers, timeout and error kinds, `feature-config.ts` reads each tool's model from `user_settings.feature_configs` (the model picker, `components/model-picker.tsx`, writes it back through `settings.tsx`'s serial queue in the website's canonical shape; tools read it through the same queue, so they run with the model just picked), `context-request.ts` builds the Current Debate context from `context-plan.ts`, which reads a debate's list of material when a form opens, and the text only of the items a request sends (kept in `context-texts.ts` under each row's version: a case's generatedAt, a chat's message count, a round's updated_at; source text is read again after each list read, and `updateResult` and `saveChat` forget what they change), and plans it with the website's planner (`packages/core/src/debate/context-plan.ts`), with the Review choices for one request (Crossfire keeps them for its round) and the pins in `debate_context_pins` (three at most); the line on each tool form, Crossfire, Sparring and Ask (`components/context-line.tsx`) and its Review sheet read the same plan, and `chats.ts` saves finished exchanges to `chats` and `messages`. The SSE parser is shared in `packages/core/src/ai/sse.ts`. `markdown.ts` reads answers, because react-markdown needs the DOM.
- Generate and Attack mirror `generateCase` and `attackCase` in `src/lib/ai/client.ts` and the session store's `recordResult`: `results.ts` sends the requests through `ai-request.ts`'s `postAI`, which streams them as the website does (`stream: true` with a `resultKey`; the reader is `packages/core/src/ai/live.ts`, shared with the website's `src/lib/ai/live.ts`) and reads a dropped stream's answer back from `ai_run_results` (`packages/core/src/ai/run-recovery.ts`). The four tool screens show the draft with `components/live-run.tsx` (the website's `LiveToolRun`; the sections come from `packages/core/src/ai/live-sections.ts`). `results.ts` also checks the output with the website's transform (moved to `packages/core/src/ai/generate-output.ts`), keeps researched sources in the debate's Auto-saved folder (`packages/core/src/ai/discovered-sources.ts`), saves the server's source rankings onto the Library sources a result used (`packages/core/src/ai/source-assessments.ts`, also for Ask's whole answers), says when the server shortened the input, and saves rows to `cases` and `attacks`. `motion.ts` and `motion-gate.ts` are the website's motion gate. `open-results.ts` holds results made in this session, so one-off work and failed saves still open as documents. It and the last Generate and Attack forms live in `session-memory.ts`, which the auth provider clears on every account change, including sign-out. `components/document.tsx` draws results as documents (mock 03); Brainstorm and Factoids reuse it. Export renders `export-html.ts` to a PDF with expo-print and shares it (`export.ts`); the web preview cannot print, so `export.web.ts` keeps the HTML and copied text on `window` for the screenshot script. Word export and Google Docs are not in the app.
- Brainstorm and Factoids mirror `brainstorm` and `factoids` in `src/lib/ai/client.ts` and their pages, through the same `results.ts`: the brainstorm transform and the verified-factoid selection live in `packages/core/src/ai/brainstorm-output.ts` and `factoids-output.ts`, rows go to `brainstorms` and `factoids`. Brainstorm has the motion gate; Factoids, like Attack, follows the Current Debate and saves with side `neutral`. A factoid's bookmark saves it to the Current Debate's `Factoids` folder, and a search suggestion reopens the Factoids form with that query. `prepareToolHref` in `components/prepare-tools.ts` is where both tabs open a tool.
Expand Down Expand Up @@ -59,4 +63,4 @@ ONLY=offline PORT=18080 STAGING_DEMO_CODE=... npx tsx apps/mobile/scripts/screen

Screenshots go to `.verify/mobile-shots/`. For signed-in screens, see the header of `scripts/screenshots.ts`. To load the app on a phone, see [docs/development.md](../../docs/development.md#mobile-app).

knip ignores `expo-updates`: its Expo plugin expects the package whenever the app config does not disable updates, and EAS Update is not set up yet. It also ignores `expo-speech-recognition` and `expo-widgets`, which the app reaches by native module name, not by import.
knip ignores `expo-updates`: its Expo plugin expects the package whenever the app config does not disable updates, and EAS Update is not set up yet. It also ignores `expo-speech-recognition` and `expo-widgets`, which the app reaches by native module name, not by import, and `@expo/dom-webview`, which Expo's DOM components load by name.
3 changes: 3 additions & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@debatelab/core": "*",
"@expo-google-fonts/fredoka": "^0.4.1",
"@expo-google-fonts/nunito": "^0.4.2",
"@expo/dom-webview": "~57.0.1",
"@formatjs/intl-listformat": "^8.3.15",
"@formatjs/intl-pluralrules": "^6.3.15",
"@react-native-async-storage/async-storage": "2.2.0",
Expand Down Expand Up @@ -44,6 +45,8 @@
"expo-web-browser": "~57.0.3",
"expo-widgets": "~57.0.24",
"i18next": "^23.16.8",
"mammoth": "^1.12.0",
"pdfjs-dist": "^6.3.289",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-i18next": "^15.4.1",
Expand Down
Binary file not shown.
Binary file added apps/mobile/scripts/fixtures/rebuttal-notes.docx
Binary file not shown.
Binary file added apps/mobile/scripts/fixtures/whiteboard-photo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading