diff --git a/apps/mobile/src/MobileApp.tsx b/apps/mobile/src/MobileApp.tsx
index ecfc478a..afc03e68 100644
--- a/apps/mobile/src/MobileApp.tsx
+++ b/apps/mobile/src/MobileApp.tsx
@@ -18,6 +18,7 @@ import {
SlidersHorizontal,
} from 'phosphor-react-native'
import { MobileNote, notes, sidebarSections } from './demoData'
+import { MobileEditorAdapter } from './MobileEditorAdapter'
import {
createCompactNavigationState,
transitionCompactNavigation,
@@ -213,7 +214,6 @@ function EditorPanel({
onBack?: () => void
onOpenProperties?: () => void
}) {
- const bodyLines = note.content.split('\n').filter((line) => line.trim() && !line.startsWith('---') && !line.includes(': '))
return (
@@ -222,19 +222,7 @@ function EditorPanel({
{onOpenProperties ? } onPress={onOpenProperties} /> : null}
} />
-
-
- {note.type}
- /
- {note.id}
-
- {note.title}
- {bodyLines.slice(1).map((line) => (
-
- {line}
-
- ))}
-
+
)
}
diff --git a/apps/mobile/src/MobileEditorAdapter.tsx b/apps/mobile/src/MobileEditorAdapter.tsx
new file mode 100644
index 00000000..2020f486
--- /dev/null
+++ b/apps/mobile/src/MobileEditorAdapter.tsx
@@ -0,0 +1,25 @@
+import { useMemo } from 'react'
+import { ScrollView, Text, View } from 'react-native'
+import type { MobileNote } from './mobileNoteProjection'
+import { createMobileEditorDocument } from './mobileEditorDocument'
+import { styles } from './styles'
+
+export function MobileEditorAdapter({ note }: { note: MobileNote }) {
+ const document = useMemo(() => createMobileEditorDocument(note), [note])
+
+ return (
+
+
+ {note.type}
+ /
+ {note.id}
+
+ {document.title}
+ {document.blocks.map((block) => (
+
+ {block.text}
+
+ ))}
+
+ )
+}
diff --git a/apps/mobile/src/mobileEditorDocument.test.ts b/apps/mobile/src/mobileEditorDocument.test.ts
new file mode 100644
index 00000000..a85bb633
--- /dev/null
+++ b/apps/mobile/src/mobileEditorDocument.test.ts
@@ -0,0 +1,65 @@
+import { describe, expect, it } from 'vitest'
+import { createMobileEditorDocument } from './mobileEditorDocument'
+
+describe('mobile editor document', () => {
+ it('strips frontmatter and title heading from the displayed editor body', () => {
+ expect(
+ createMobileEditorDocument({
+ title: 'Workflow Orchestration Essay',
+ content: [
+ '---',
+ 'type: Essay',
+ '---',
+ '',
+ '# Workflow Orchestration Essay',
+ '',
+ 'The current narrative: everything routed through an LLM.',
+ ].join('\n'),
+ }),
+ ).toEqual({
+ title: 'Workflow Orchestration Essay',
+ blocks: [
+ {
+ id: '0:The current narrative: everything routed through an LLM.',
+ kind: 'paragraph',
+ text: 'The current narrative: everything routed through an LLM.',
+ },
+ ],
+ })
+ })
+
+ it('keeps colon paragraphs instead of treating them as frontmatter', () => {
+ const document = createMobileEditorDocument({
+ title: 'Notes for Monday',
+ content: '# Notes for Monday\n\nBottom line up front: ship the smallest useful slice.',
+ })
+
+ expect(document.blocks).toEqual([
+ {
+ id: '0:Bottom line up front: ship the smallest useful slice.',
+ kind: 'paragraph',
+ text: 'Bottom line up front: ship the smallest useful slice.',
+ },
+ ])
+ })
+
+ it('normalizes markdown bullets for the native placeholder surface', () => {
+ const document = createMobileEditorDocument({
+ title: 'Plan',
+ content: '# Plan\n\n- Sidebar\n* Note list',
+ })
+
+ expect(document.blocks).toEqual([
+ {
+ id: '0:- Sidebar',
+ kind: 'bullet',
+ text: 'Sidebar',
+ },
+ {
+ id: '1:* Note list',
+ kind: 'bullet',
+ text: 'Note list',
+ },
+ ])
+ })
+})
diff --git a/apps/mobile/src/mobileEditorDocument.ts b/apps/mobile/src/mobileEditorDocument.ts
new file mode 100644
index 00000000..1a2762eb
--- /dev/null
+++ b/apps/mobile/src/mobileEditorDocument.ts
@@ -0,0 +1,53 @@
+import { splitFrontmatter } from '@tolaria/markdown'
+
+export type MobileEditorBlock = {
+ id: string
+ kind: 'bullet' | 'paragraph'
+ text: string
+}
+
+export type MobileEditorDocument = {
+ title: string
+ blocks: MobileEditorBlock[]
+}
+
+export type MobileEditorDocumentInput = {
+ title: string
+ content: string
+}
+
+export function createMobileEditorDocument(input: MobileEditorDocumentInput): MobileEditorDocument {
+ const [, body] = splitFrontmatter(input.content)
+
+ return {
+ title: input.title,
+ blocks: createBlocks(body, input.title),
+ }
+}
+
+function createBlocks(body: string, title: string) {
+ return body
+ .split('\n')
+ .map((line) => line.trim())
+ .filter((line) => line && !isTitleHeading(line, title))
+ .map(createBlock)
+}
+
+function createBlock(line: string, index: number): MobileEditorBlock {
+ const bulletText = bulletContent(line)
+
+ return {
+ id: `${index}:${line}`,
+ kind: bulletText ? 'bullet' : 'paragraph',
+ text: bulletText ?? line,
+ }
+}
+
+function bulletContent(line: string) {
+ const match = /^[-*]\s+(.+)$/.exec(line)
+ return match?.[1] ?? null
+}
+
+function isTitleHeading(line: string, title: string) {
+ return line === `# ${title}`
+}
diff --git a/docs/MOBILE_PROGRESS.md b/docs/MOBILE_PROGRESS.md
index 6d2cbfd2..9dcf1cc5 100644
--- a/docs/MOBILE_PROGRESS.md
+++ b/docs/MOBILE_PROGRESS.md
@@ -8,7 +8,7 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Branch: `codex/mobile`
- Active phase: Phase 2 - Mobile Shell
-- Active slice: Connect parsed Git remotes to mobile vault configuration
+- Active slice: Add mobile editor adapter boundary for TenTap
- Push policy: commit locally; do not push unless explicitly requested
- Validation target: iPad/iOS simulator first
@@ -45,13 +45,14 @@ This file is the resumable working log for Tolaria mobile. The strategy and road
- Re-tested the iPad simulator path; the app now launches in Expo Go on `iPad Pro 13-inch (M4)`.
- Added a pure mobile Git remote parser that codifies the auth choice: GitHub remotes use the GitHub OAuth App path, arbitrary Git remotes use the SSH-key path.
- Added a pure mobile vault configuration model that keeps vault storage app-local, distinguishes local-only vs remote-backed sync, and derives the required Git auth path from the parsed remote.
+- Extracted the mobile editor surface behind `MobileEditorAdapter` with a tested document projection so TenTap can replace the placeholder surface without changing shell navigation.
## Next Action
Continue Phase 2 with the next mobile shell slice:
1. Dismiss or suppress Expo Go's first-run tools modal during simulator QA so screenshots capture the app without the overlay.
-2. Begin the TenTap editor spike behind a `MobileEditorAdapter` once the shell/storage boundary is stable.
+2. Install and spike TenTap behind `MobileEditorAdapter`, keeping the adapter contract intact.
3. Add the first native storage adapter around the vault config/repository contracts after the editor spike confirms the app shape.
## Verification Log
@@ -124,6 +125,10 @@ Continue Phase 2 with the next mobile shell slice:
- `pnpm --filter @tolaria/mobile typecheck` passed after vault config extraction.
- CodeScene after vault config extraction: `apps/mobile/src/mobileVaultConfig.ts` and `apps/mobile/src/mobileVaultConfig.test.ts` scored `10`.
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after vault config extraction.
+- `pnpm --filter @tolaria/mobile test -- src/mobileEditorDocument.test.ts` passed after editor adapter extraction: 8 files / 27 tests.
+- `pnpm --filter @tolaria/mobile typecheck` passed after editor adapter extraction.
+- CodeScene after editor adapter extraction: `apps/mobile/src/MobileApp.tsx`, `apps/mobile/src/MobileEditorAdapter.tsx`, `apps/mobile/src/mobileEditorDocument.ts`, and `apps/mobile/src/mobileEditorDocument.test.ts` scored `10`.
+- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after editor adapter extraction.
## Risks / Watch Items