From b6fed40c263227f552789453fe3ed26097844344 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 19 Jun 2026 01:26:29 -0700 Subject: [PATCH 1/3] Unify mobile typography tokens - Add shared mobile typography scale for native and web styles - Replace hard-coded text sizes across mobile screens and components - Keep code surface, markdown, and typography tests aligned Co-authored-by: codex --- apps/mobile/global.css | 22 ++++++++++- apps/mobile/src/app/+not-found.tsx | 4 +- apps/mobile/src/app/connections/index.tsx | 2 +- apps/mobile/src/app/connections/new.tsx | 10 ++--- apps/mobile/src/app/new/index.tsx | 12 +++--- apps/mobile/src/app/settings/environments.tsx | 22 +++++------ apps/mobile/src/app/settings/index.tsx | 14 +++---- apps/mobile/src/components/AppText.tsx | 2 +- apps/mobile/src/components/BrandMark.tsx | 9 ++--- .../src/components/ComposerToolbarTrigger.tsx | 2 +- apps/mobile/src/components/ControlPill.tsx | 2 +- apps/mobile/src/components/EmptyState.tsx | 4 +- apps/mobile/src/components/ErrorBanner.tsx | 2 +- apps/mobile/src/components/StatusPill.tsx | 2 +- .../archive/ArchivedThreadsScreen.tsx | 20 +++++----- .../cloud/CloudWaitlistEnrollment.tsx | 20 ++++------ .../connection/ConnectionEnvironmentRow.tsx | 21 +++++------ .../connection/ConnectionSheetButton.tsx | 2 +- .../EnvironmentConnectionNotice.tsx | 6 +-- .../features/files/FileMarkdownPreview.tsx | 7 ++-- .../src/features/files/FileTreeBrowser.tsx | 14 +++---- .../src/features/files/SourceFileSurface.tsx | 24 +++++++----- .../features/files/ThreadFilesRouteScreen.tsx | 15 ++++---- .../files/WorkspaceFileImagePreview.tsx | 4 +- .../files/WorkspaceFileWebPreview.tsx | 8 ++-- .../files/nativeSourceFileAdapter.test.ts | 20 ++++++++++ .../features/files/nativeSourceFileAdapter.ts | 18 +++++---- apps/mobile/src/features/home/HomeHeader.tsx | 5 ++- apps/mobile/src/features/home/HomeScreen.tsx | 14 +++---- .../features/home/thread-swipe-actions.tsx | 2 +- .../features/projects/AddProjectScreen.tsx | 22 +++++------ .../review/ReviewCommentComposerSheet.tsx | 16 ++++---- .../src/features/review/ReviewSheet.tsx | 37 ++++++++++--------- .../review/nativeReviewDiffAdapter.ts | 13 ++++--- .../features/review/reviewDiffRendering.tsx | 19 +++++++--- .../terminal/NativeTerminalSurface.tsx | 11 +++--- .../features/terminal/ThreadTerminalPanel.tsx | 6 +-- .../terminal/ThreadTerminalRouteScreen.tsx | 5 ++- .../threads/ComposerCommandPopover.tsx | 17 +++++++-- .../threads/GitActionProgressOverlay.tsx | 4 +- .../features/threads/NewTaskDraftScreen.tsx | 6 ++- .../features/threads/PendingApprovalCard.tsx | 2 +- .../features/threads/PendingUserInputCard.tsx | 8 ++-- .../src/features/threads/ThreadComposer.tsx | 10 ++--- .../src/features/threads/ThreadFeed.tsx | 26 ++++++------- .../threads/ThreadNavigationDrawer.tsx | 12 +++--- .../features/threads/ThreadRouteScreen.tsx | 5 ++- .../features/threads/git/GitBranchesSheet.tsx | 22 +++++------ .../features/threads/git/GitCommitSheet.tsx | 36 +++++++++--------- .../features/threads/git/GitConfirmSheet.tsx | 6 +-- .../features/threads/git/GitOverviewSheet.tsx | 6 +-- .../threads/git/gitSheetComponents.tsx | 10 ++--- .../src/features/threads/thread-work-log.tsx | 6 +-- apps/mobile/src/lib/typography.test.ts | 19 ++++++++++ apps/mobile/src/lib/typography.ts | 21 +++++++++++ apps/mobile/src/native/T3ComposerEditor.tsx | 4 +- 56 files changed, 376 insertions(+), 282 deletions(-) create mode 100644 apps/mobile/src/lib/typography.test.ts create mode 100644 apps/mobile/src/lib/typography.ts diff --git a/apps/mobile/global.css b/apps/mobile/global.css index 0fbf4fb3c9d..b2014bf9353 100644 --- a/apps/mobile/global.css +++ b/apps/mobile/global.css @@ -192,11 +192,31 @@ } } -/* ─── Font family ───────────────────────────────────────────────────── */ +/* ─── Typography ────────────────────────────────────────────────────── */ @theme { --font-sans: "DMSans_400Regular"; --font-medium: "DMSans_500Medium"; --font-bold: "DMSans_700Bold"; + + /* Keep this scale aligned with src/lib/typography.ts for native style props. */ + --text-3xs: 10px; + --text-3xs--line-height: 13px; + --text-2xs: 11px; + --text-2xs--line-height: 15px; + --text-xs: 12px; + --text-xs--line-height: 16px; + --text-sm: 13px; + --text-sm--line-height: 18px; + --text-base: 15px; + --text-base--line-height: 22px; + --text-lg: 17px; + --text-lg--line-height: 22px; + --text-xl: 20px; + --text-xl--line-height: 26px; + --text-2xl: 24px; + --text-2xl--line-height: 30px; + --text-3xl: 28px; + --text-3xl--line-height: 34px; } /* ─── Custom utilities ──────────────────────────────────────────────── */ diff --git a/apps/mobile/src/app/+not-found.tsx b/apps/mobile/src/app/+not-found.tsx index 124077b0909..d11155f8602 100644 --- a/apps/mobile/src/app/+not-found.tsx +++ b/apps/mobile/src/app/+not-found.tsx @@ -21,7 +21,7 @@ export default function NotFoundRoute() { }} style={[{ flex: 1 }, screenBgStyle]} > - + Route not found @@ -35,7 +35,7 @@ export default function NotFoundRoute() { primaryBgStyle, ]} > - Return home + Return home diff --git a/apps/mobile/src/app/connections/index.tsx b/apps/mobile/src/app/connections/index.tsx index 5db76f1c6b1..12a06996447 100644 --- a/apps/mobile/src/app/connections/index.tsx +++ b/apps/mobile/src/app/connections/index.tsx @@ -85,7 +85,7 @@ export default function ConnectionsRouteScreen() { type="monochrome" /> - + No environments connected yet.{"\n"}Tap{" "} + to add one. diff --git a/apps/mobile/src/app/connections/new.tsx b/apps/mobile/src/app/connections/new.tsx index cf1f6a7f7e5..ca9693dbb19 100644 --- a/apps/mobile/src/app/connections/new.tsx +++ b/apps/mobile/src/app/connections/new.tsx @@ -171,7 +171,7 @@ export default function ConnectionsNewRouteScreen() { className="items-center gap-3 rounded-[24px] bg-card px-5 py-8" style={{ borderCurve: "continuous" }} > - + Camera permission is required to scan a QR code. Host @@ -202,13 +202,13 @@ export default function ConnectionsNewRouteScreen() { placeholderTextColor={placeholderColor} value={hostInput} onChangeText={handleHostChange} - className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-[15px] text-foreground" + className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-base text-foreground" /> Pairing code @@ -220,7 +220,7 @@ export default function ConnectionsNewRouteScreen() { placeholderTextColor={placeholderColor} value={codeInput} onChangeText={handleCodeChange} - className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-[15px] text-foreground" + className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-base text-foreground" /> diff --git a/apps/mobile/src/app/new/index.tsx b/apps/mobile/src/app/new/index.tsx index dbde9c4a412..6e2aa64ce11 100644 --- a/apps/mobile/src/app/new/index.tsx +++ b/apps/mobile/src/app/new/index.tsx @@ -129,10 +129,10 @@ export default function NewTaskRoute() { {items.length === 0 ? ( {projectEmptyState.loading ? : null} - + {projectEmptyState.title} - + {projectEmptyState.detail} {!catalogState.hasReadyEnvironment ? ( @@ -140,7 +140,7 @@ export default function NewTaskRoute() { className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70" onPress={() => router.push("/connections/new")} > - + Add environment @@ -149,7 +149,7 @@ export default function NewTaskRoute() { className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70" onPress={() => router.push("/new/add-project")} > - + Add new project @@ -197,9 +197,7 @@ export default function NewTaskRoute() { /> - - {item.title} - + {item.title} - + No environments connected yet.{"\n"}Tap{" "} + to add one. @@ -160,7 +160,7 @@ function ConfiguredCloudEnvironmentRows(props: { return ( - T3 Cloud + T3 Cloud - + Loading linked cloud environments. ) : controller.relayDiscovery.error ? ( - + Could not load T3 Cloud environments - + {controller.relayDiscovery.error} {controller.relayDiscovery.errorTraceId ? ( @@ -222,7 +222,7 @@ function ConfiguredCloudEnvironmentRows(props: { ) : ( - + No additional linked cloud environments. @@ -361,7 +361,7 @@ function CloudEnvironmentRowShell(props: { {props.label} @@ -370,7 +370,7 @@ function CloudEnvironmentRowShell(props: { {props.connectionError ? ( @@ -384,7 +384,7 @@ function CloudEnvironmentRowShell(props: { className="min-w-0 flex-row items-start gap-1" > {statusText} @@ -394,7 +394,7 @@ function CloudEnvironmentRowShell(props: { { event.stopPropagation(); copyTextWithHaptic(errorTraceId); @@ -446,7 +446,7 @@ function CopyTraceIdButton(props: { readonly traceId: string }) { className="self-start flex-row items-center gap-1.5 rounded-full bg-subtle px-3 py-2 active:opacity-70" > - Copy trace ID + Copy trace ID ); } diff --git a/apps/mobile/src/app/settings/index.tsx b/apps/mobile/src/app/settings/index.tsx index eae7c5fa33a..41799ae7b8b 100644 --- a/apps/mobile/src/app/settings/index.tsx +++ b/apps/mobile/src/app/settings/index.tsx @@ -346,7 +346,7 @@ function ConfiguredSettingsRouteScreen() { onPress={openAccount} /> - + T3 Code works locally without signing in. Cloud features are optional. @@ -389,7 +389,7 @@ type SymbolName = ComponentProps["name"]; function SettingsSection(props: { readonly title: string; readonly children: ReactNode }) { return ( - {props.title} + {props.title} - Version - Alpha + Version + Alpha ); @@ -444,13 +444,13 @@ function SettingsRow(props: { style={{ opacity: props.disabled ? 0.45 : 1 }} > - + {props.label} {props.value ? ( @@ -502,7 +502,7 @@ function SettingsSwitchRow(props: { style={{ opacity: props.disabled ? 0.45 : 1 }} > - {props.label} + {props.label} - + T3 Code {stageLabel} @@ -38,7 +35,7 @@ export function BrandMark(props: { readonly compact?: boolean; readonly stageLab {!compact ? ( - + Mobile control surface for your live coding environments ) : null} diff --git a/apps/mobile/src/components/ComposerToolbarTrigger.tsx b/apps/mobile/src/components/ComposerToolbarTrigger.tsx index 7cb93454f88..e054a13f697 100644 --- a/apps/mobile/src/components/ComposerToolbarTrigger.tsx +++ b/apps/mobile/src/components/ComposerToolbarTrigger.tsx @@ -223,7 +223,7 @@ export function ComposerToolbarButton(props: { {props.label ? ( - - {props.actionLabel} - + {props.actionLabel} ) : null} diff --git a/apps/mobile/src/components/ErrorBanner.tsx b/apps/mobile/src/components/ErrorBanner.tsx index 3fb8ba5d917..d47f924b398 100644 --- a/apps/mobile/src/components/ErrorBanner.tsx +++ b/apps/mobile/src/components/ErrorBanner.tsx @@ -4,7 +4,7 @@ import { AppText as Text } from "./AppText"; export function ErrorBanner(props: { readonly message: string }) { return ( - + {props.message} diff --git a/apps/mobile/src/components/StatusPill.tsx b/apps/mobile/src/components/StatusPill.tsx index 34e6f74b609..03985463aa8 100644 --- a/apps/mobile/src/components/StatusPill.tsx +++ b/apps/mobile/src/components/StatusPill.tsx @@ -26,7 +26,7 @@ export function StatusPill( diff --git a/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx b/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx index ecdd9990186..fa89784c86d 100644 --- a/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx +++ b/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx @@ -147,14 +147,14 @@ function ProjectGroupLabel(props: { workspaceRoot={props.project.workspaceRoot} /> {props.project.title} {props.environmentLabel ? ( - + {props.environmentLabel} ) : null} @@ -265,13 +265,13 @@ function ArchivedThreadRow(props: { {props.thread.title} {timestamp} @@ -286,7 +286,7 @@ function ArchivedThreadRow(props: { type="monochrome" /> @@ -314,10 +314,12 @@ function ArchivedThreadRow(props: { function ArchiveError(props: { readonly message: string; readonly onRetry: () => void }) { return ( - Could not load every archive - {props.message} + + Could not load every archive + + {props.message} - Try again + Try again ); @@ -386,7 +388,7 @@ export function ArchivedThreadsScreen(props: { {isInitialLoad ? ( - Loading archive… + Loading archive… ) : props.groups.length === 0 ? ( void }) { @@ -141,12 +142,11 @@ function useCloudWaitlistColors() { const styles = StyleSheet.create({ body: { fontFamily: "DMSans_400Regular", - fontSize: 15, - lineHeight: 21, + ...MOBILE_TYPOGRAPHY.body, }, buttonText: { fontFamily: "DMSans_700Bold", - fontSize: 16, + fontSize: MOBILE_TYPOGRAPHY.body.fontSize, }, content: { gap: 18, @@ -156,8 +156,7 @@ const styles = StyleSheet.create({ }, error: { fontFamily: "DMSans_400Regular", - fontSize: 13, - lineHeight: 18, + ...MOBILE_TYPOGRAPHY.footnote, }, field: { gap: 8, @@ -167,15 +166,14 @@ const styles = StyleSheet.create({ borderRadius: 16, borderWidth: 1, fontFamily: "DMSans_400Regular", - fontSize: 17, + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, minHeight: 54, paddingHorizontal: 16, paddingVertical: 14, }, label: { fontFamily: "DMSans_700Bold", - fontSize: 13, - lineHeight: 18, + ...MOBILE_TYPOGRAPHY.footnote, }, primaryButton: { alignItems: "center", @@ -196,13 +194,11 @@ const styles = StyleSheet.create({ }, signInText: { fontFamily: "DMSans_700Bold", - fontSize: 15, - lineHeight: 21, + ...MOBILE_TYPOGRAPHY.body, }, title: { fontFamily: "DMSans_700Bold", - fontSize: 20, - lineHeight: 26, + ...MOBILE_TYPOGRAPHY.title, textAlign: "center", }, }); diff --git a/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx b/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx index 5c3e290fa22..f5aa26be960 100644 --- a/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx +++ b/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx @@ -76,19 +76,16 @@ export function ConnectionEnvironmentRow(props: { /> - + {props.environment.environmentLabel} - + {props.environment.displayUrl} {statusLabel ? ( {props.environment.isRelayManaged ? ( - + Managed by T3 Cloud. Tunnel details update automatically. ) : ( <> Label @@ -156,13 +153,13 @@ export function ConnectionEnvironmentRow(props: { placeholderTextColor={placeholderColor} value={label} onChangeText={setLabel} - className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-[15px] text-foreground" + className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-base text-foreground" /> URL @@ -175,7 +172,7 @@ export function ConnectionEnvironmentRow(props: { placeholderTextColor={placeholderColor} value={url} onChangeText={setUrl} - className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-[15px] text-foreground" + className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-base text-foreground" /> @@ -189,7 +186,7 @@ export function ConnectionEnvironmentRow(props: { > Save diff --git a/apps/mobile/src/features/connection/ConnectionSheetButton.tsx b/apps/mobile/src/features/connection/ConnectionSheetButton.tsx index 1a03061e23f..8a692d80729 100644 --- a/apps/mobile/src/features/connection/ConnectionSheetButton.tsx +++ b/apps/mobile/src/features/connection/ConnectionSheetButton.tsx @@ -104,7 +104,7 @@ export function ConnectionSheetButton(props: { type="monochrome" /> {props.label} diff --git a/apps/mobile/src/features/connection/EnvironmentConnectionNotice.tsx b/apps/mobile/src/features/connection/EnvironmentConnectionNotice.tsx index 15852cc3c88..9b8c96d25ea 100644 --- a/apps/mobile/src/features/connection/EnvironmentConnectionNotice.tsx +++ b/apps/mobile/src/features/connection/EnvironmentConnectionNotice.tsx @@ -73,10 +73,10 @@ export function EnvironmentConnectionNotice(props: { /> )} - + {noticeTitle(props.connection.phase, props.environmentLabel)} - + {noticeDetail(props.connection.phase, props.resourceName, props.connection.error)} {props.connection.traceId ? ( <> @@ -99,7 +99,7 @@ export function EnvironmentConnectionNotice(props: { className="mt-1 rounded-full bg-subtle px-4 py-2.5 active:opacity-70" onPress={props.onRetry} > - Retry now + Retry now ) : null} diff --git a/apps/mobile/src/features/files/FileMarkdownPreview.tsx b/apps/mobile/src/features/files/FileMarkdownPreview.tsx index 1f6720cb7db..469a4c983a9 100644 --- a/apps/mobile/src/features/files/FileMarkdownPreview.tsx +++ b/apps/mobile/src/features/files/FileMarkdownPreview.tsx @@ -7,6 +7,7 @@ import { } from "react-native-nitro-markdown"; import { Linking, ScrollView, Text as NativeText, View } from "react-native"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { useThemeColor } from "../../lib/useThemeColor"; import { hasNativeSelectableMarkdownText, @@ -72,8 +73,7 @@ function useMarkdownPreviewStyles(): MarkdownPreviewStyles { text: { color: body, fontFamily: "DMSans_400Regular", - fontSize: 15, - lineHeight: 22, + ...MOBILE_TYPOGRAPHY.body, }, heading: { color: strong, @@ -123,8 +123,7 @@ function useMarkdownPreviewStyles(): MarkdownPreviewStyles { skillTextColor: codeText, quoteMarkerColor: blockquoteBorder, dividerColor: horizontalRule, - fontSize: 15, - lineHeight: 22, + ...MOBILE_TYPOGRAPHY.body, fontFamily: "DMSans_400Regular", headingFontFamily: "DMSans_700Bold", boldFontFamily: "DMSans_700Bold", diff --git a/apps/mobile/src/features/files/FileTreeBrowser.tsx b/apps/mobile/src/features/files/FileTreeBrowser.tsx index ff9577a4adb..3def77433b2 100644 --- a/apps/mobile/src/features/files/FileTreeBrowser.tsx +++ b/apps/mobile/src/features/files/FileTreeBrowser.tsx @@ -64,7 +64,7 @@ const FileTreeRow = memo(function FileTreeRow(props: { {node.kind === "directory" ? ( - + {node.children.length} ) : null} @@ -142,10 +142,8 @@ export function FileTreeBrowser(props: { {props.error && props.entries.length === 0 ? ( - Files unavailable - - {props.error} - + Files unavailable + {props.error} ) : ( ) : ( <> - No files found - + No files found + {props.searchQuery.trim().length > 0 ? "Try a different search." : "The workspace file index is empty."} diff --git a/apps/mobile/src/features/files/SourceFileSurface.tsx b/apps/mobile/src/features/files/SourceFileSurface.tsx index 5f3647d735f..b96d6515951 100644 --- a/apps/mobile/src/features/files/SourceFileSurface.tsx +++ b/apps/mobile/src/features/files/SourceFileSurface.tsx @@ -18,6 +18,7 @@ import { } from "../review/reviewDiffRendering"; import type { ReviewHighlightedToken } from "../review/shikiReviewHighlighter"; import { cn } from "../../lib/cn"; +import { MOBILE_CODE_SURFACE } from "../../lib/typography"; import { buildNativeSourceRows, buildNativeSourceTokens, @@ -28,8 +29,8 @@ import { } from "./nativeSourceFileAdapter"; import { sourceHighlightAtom } from "./sourceHighlightingState"; -const SOURCE_LINE_HEIGHT = 24; -const SOURCE_LINE_NUMBER_WIDTH = 58; +const SOURCE_LINE_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; +const SOURCE_LINE_NUMBER_WIDTH = MOBILE_CODE_SURFACE.gutterWidth; const NATIVE_SOURCE_STYLE_JSON = JSON.stringify(NATIVE_SOURCE_STYLE); interface SourceFileSurfaceProps { @@ -56,10 +57,12 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { style={{ minHeight: SOURCE_LINE_HEIGHT }} > {props.index + 1} @@ -67,8 +70,13 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { {props.tokens && props.tokens.length > 0 ? (() => { @@ -80,7 +88,7 @@ const HighlightedSourceLine = memo(function HighlightedSourceLine(props: { const fontWeight = token.fontStyle !== null && (token.fontStyle & 2) === 2 ? ("700" as const) - : ("500" as const); + : ("400" as const); const fontStyle = token.fontStyle !== null && (token.fontStyle & 1) === 1 ? ("italic" as const) @@ -142,9 +150,7 @@ function SourceHighlightStatusView(props: { readonly status: SourceHighlightStat if (props.status === "error") { return ( - - Plain text - + Plain text ); } diff --git a/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx b/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx index f730e4616ac..5cd7fe9d02b 100644 --- a/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx +++ b/apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx @@ -24,6 +24,7 @@ import { EmptyState } from "../../components/EmptyState"; import { LoadingScreen } from "../../components/LoadingScreen"; import { cn } from "../../lib/cn"; import { buildThreadFilesNavigation } from "../../lib/routes"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { useThemeColor } from "../../lib/useThemeColor"; import { useThreadSelection } from "../../state/use-thread-selection"; import { useSelectedThreadWorktree } from "../../state/use-selected-thread-worktree"; @@ -100,7 +101,7 @@ function ModeButton(props: { @@ -187,7 +188,7 @@ function FileBreadcrumbs(props: { readonly projectName: string; readonly relativ ) : null} - Loading file... + Loading file... ); } @@ -317,10 +318,10 @@ function FileContent(props: { {props.truncated ? ( - + Partial file - + Preview limited to the first 1 MB of a truncated file. @@ -389,7 +390,7 @@ function FilesHeaderTitle(props: { readonly projectName: string }) { style={{ color: foregroundColor, fontFamily: "DMSans_700Bold", - fontSize: 18, + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, fontWeight: "900", letterSpacing: -0.4, }} @@ -401,7 +402,7 @@ function FilesHeaderTitle(props: { readonly projectName: string }) { style={{ color: secondaryForegroundColor, fontFamily: "DMSans_500Medium", - fontSize: 12, + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, fontWeight: "500", letterSpacing: 0.2, }} diff --git a/apps/mobile/src/features/files/WorkspaceFileImagePreview.tsx b/apps/mobile/src/features/files/WorkspaceFileImagePreview.tsx index 15d5b76717d..73eca66bf99 100644 --- a/apps/mobile/src/features/files/WorkspaceFileImagePreview.tsx +++ b/apps/mobile/src/features/files/WorkspaceFileImagePreview.tsx @@ -81,7 +81,7 @@ function CachedWorkspaceFileImagePreview(props: { return ( - Loading image... + Loading image... ); } @@ -102,7 +102,7 @@ export function WorkspaceFileImagePreview(props: { return ( - + Preparing image preview... diff --git a/apps/mobile/src/features/files/WorkspaceFileWebPreview.tsx b/apps/mobile/src/features/files/WorkspaceFileWebPreview.tsx index 1628c4601d0..6d03a23d52a 100644 --- a/apps/mobile/src/features/files/WorkspaceFileWebPreview.tsx +++ b/apps/mobile/src/features/files/WorkspaceFileWebPreview.tsx @@ -13,7 +13,7 @@ export function WorkspaceFileWebPreview(props: { readonly uri: string | null }) return ( - Preparing preview... + Preparing preview... ); } @@ -23,10 +23,8 @@ export function WorkspaceFileWebPreview(props: { readonly uri: string | null }) {loadProgress > 0 && loadProgress < 1 ? : null} {loadError ? ( - Preview failed - - {loadError} - + Preview failed + {loadError} ) : null} { + it("uses the same compact code typography as the diff viewer", () => { + expect(NATIVE_SOURCE_ROW_HEIGHT).toBe(NATIVE_REVIEW_DIFF_ROW_HEIGHT); + expect(NATIVE_SOURCE_STYLE).toMatchObject({ + rowHeight: NATIVE_REVIEW_DIFF_STYLE.rowHeight, + gutterWidth: NATIVE_REVIEW_DIFF_STYLE.gutterWidth, + codePadding: NATIVE_REVIEW_DIFF_STYLE.codePadding, + textVerticalInset: NATIVE_REVIEW_DIFF_STYLE.textVerticalInset, + codeFontSize: NATIVE_REVIEW_DIFF_STYLE.codeFontSize, + codeFontWeight: NATIVE_REVIEW_DIFF_STYLE.codeFontWeight, + lineNumberFontSize: NATIVE_REVIEW_DIFF_STYLE.lineNumberFontSize, + lineNumberFontWeight: NATIVE_REVIEW_DIFF_STYLE.lineNumberFontWeight, + }); + }); + it("maps plain source lines onto context rows with stable line numbers", () => { expect(buildNativeSourceRows(["const value = 1;", "\treturn value;"])).toEqual([ { diff --git a/apps/mobile/src/features/files/nativeSourceFileAdapter.ts b/apps/mobile/src/features/files/nativeSourceFileAdapter.ts index 19abc802146..9bb341e2909 100644 --- a/apps/mobile/src/features/files/nativeSourceFileAdapter.ts +++ b/apps/mobile/src/features/files/nativeSourceFileAdapter.ts @@ -3,22 +3,24 @@ import type { NativeReviewDiffStyle, NativeReviewDiffToken, } from "../diffs/nativeReviewDiffSurface"; +import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "../../lib/typography"; import type { SourceHighlightTokens } from "./sourceHighlightingState"; -export const NATIVE_SOURCE_ROW_HEIGHT = 24; +export const NATIVE_SOURCE_ROW_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; export const NATIVE_SOURCE_CONTENT_WIDTH = 32_000; export const NATIVE_SOURCE_STYLE: NativeReviewDiffStyle = { rowHeight: NATIVE_SOURCE_ROW_HEIGHT, contentWidth: NATIVE_SOURCE_CONTENT_WIDTH, changeBarWidth: 0, - gutterWidth: 58, - codePadding: 8, - codeFontSize: 13, - codeFontWeight: "medium", - lineNumberFontSize: 11, - lineNumberFontWeight: "medium", - emptyStateFontSize: 12, + gutterWidth: MOBILE_CODE_SURFACE.gutterWidth, + codePadding: MOBILE_CODE_SURFACE.codePadding, + textVerticalInset: MOBILE_CODE_SURFACE.textVerticalInset, + codeFontSize: MOBILE_CODE_SURFACE.fontSize, + codeFontWeight: "regular", + lineNumberFontSize: MOBILE_CODE_SURFACE.lineNumberFontSize, + lineNumberFontWeight: "regular", + emptyStateFontSize: MOBILE_TYPOGRAPHY.label.fontSize, emptyStateFontWeight: "medium", }; diff --git a/apps/mobile/src/features/home/HomeHeader.tsx b/apps/mobile/src/features/home/HomeHeader.tsx index 839053523a6..9757d5fbf91 100644 --- a/apps/mobile/src/features/home/HomeHeader.tsx +++ b/apps/mobile/src/features/home/HomeHeader.tsx @@ -12,6 +12,7 @@ import { Stack } from "expo-router"; import { Text as RNText, View } from "react-native"; import { useThemeColor } from "../../lib/useThemeColor"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import type { HomeProjectSortOrder } from "./homeThreadList"; export interface HomeHeaderEnvironment { @@ -118,7 +119,7 @@ export function HomeHeader(props: { @@ -196,7 +196,7 @@ function ProjectGroupLabel(props: { {hiddenCount > 0 ? ( {props.isExpanded ? "Show less" : `${hiddenCount} more`} @@ -335,7 +335,7 @@ function ThreadRow(props: { {props.thread.title} @@ -345,12 +345,12 @@ function ThreadRow(props: { className={tone.pillClassName} style={{ borderRadius: 99, paddingHorizontal: 6, paddingVertical: 2 }} > - + {tone.label} {timestamp} @@ -367,7 +367,7 @@ function ThreadRow(props: { type="monochrome" /> @@ -429,7 +429,7 @@ function StaleCatalogStatusPill(props: { weight="semibold" /> )} - + {label} diff --git a/apps/mobile/src/features/home/thread-swipe-actions.tsx b/apps/mobile/src/features/home/thread-swipe-actions.tsx index faedaed7cee..dd0e2901bba 100644 --- a/apps/mobile/src/features/home/thread-swipe-actions.tsx +++ b/apps/mobile/src/features/home/thread-swipe-actions.tsx @@ -167,7 +167,7 @@ function SwipeActionButton(props: { - {props.label} + {props.label} diff --git a/apps/mobile/src/features/projects/AddProjectScreen.tsx b/apps/mobile/src/features/projects/AddProjectScreen.tsx index 11ff7288d61..fa1f635de8d 100644 --- a/apps/mobile/src/features/projects/AddProjectScreen.tsx +++ b/apps/mobile/src/features/projects/AddProjectScreen.tsx @@ -100,7 +100,7 @@ function sourceFromParam(value: string | string[] | undefined): AddProjectRemote function SectionTitle(props: { readonly children: string }) { return ( {props.children} @@ -168,9 +168,9 @@ function ListRow(props: { {props.icon} - {props.title} + {props.title} {props.subtitle ? ( - + {props.subtitle} ) : null} @@ -202,7 +202,7 @@ function PrimaryActionButton(props: { {props.loading ? ( ) : ( - {props.label} + {props.label} )} ); @@ -215,7 +215,7 @@ function ProjectPathInput(props: { }) { return ( - No environments connected - + No environments connected + Add an environment before adding a project. router.replace("/connections/new")} className="mt-1 rounded-full bg-primary px-4 py-2.5 active:opacity-70" > - Add environment + Add environment ); @@ -565,7 +565,7 @@ export function AddProjectRepositoryScreen() { {error ? : null} : null} {repositoryTitle ? ( - {repositoryTitle} - + {repositoryTitle} + {remoteUrl} diff --git a/apps/mobile/src/features/review/ReviewCommentComposerSheet.tsx b/apps/mobile/src/features/review/ReviewCommentComposerSheet.tsx index 65255c14ff3..d35c48e8a9b 100644 --- a/apps/mobile/src/features/review/ReviewCommentComposerSheet.tsx +++ b/apps/mobile/src/features/review/ReviewCommentComposerSheet.tsx @@ -159,26 +159,26 @@ export function ReviewCommentComposerSheet() { - Add Comment + Add Comment {!target ? ( - No selection - + No selection + Select a diff line or range first. ) : ( - + {selectionLabel} @@ -215,7 +215,7 @@ export function ReviewCommentComposerSheet() { > {lineNumber ?? ""} @@ -236,7 +236,7 @@ export function ReviewCommentComposerSheet() { - Comment + Comment @@ -248,7 +248,7 @@ export function ReviewCommentComposerSheet() { textAlignVertical="top" value={commentText} onChangeText={setCommentText} - className="h-full flex-1 border-0 bg-transparent px-0 py-0 font-sans text-[15px]" + className="h-full flex-1 border-0 bg-transparent px-0 py-0 font-sans text-base" style={{ flex: 1, minHeight: 0 }} /> diff --git a/apps/mobile/src/features/review/ReviewSheet.tsx b/apps/mobile/src/features/review/ReviewSheet.tsx index c02120b2619..92203c0ed4e 100644 --- a/apps/mobile/src/features/review/ReviewSheet.tsx +++ b/apps/mobile/src/features/review/ReviewSheet.tsx @@ -20,6 +20,7 @@ import { environmentCatalog } from "../../connection/catalog"; import { useEnvironmentPresentation } from "../../state/presentation"; import { useAtomCommand } from "../../state/use-atom-command"; import { useThemeColor } from "../../lib/useThemeColor"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { useThreadDraftForThread } from "../../state/use-thread-composer-state"; import { EnvironmentConnectionNotice } from "../connection/EnvironmentConnectionNotice"; import { useReviewCacheForThread } from "./reviewState"; @@ -41,10 +42,10 @@ const REVIEW_HEADER_SPACING = 0; const ReviewNotice = memo(function ReviewNotice(props: { readonly notice: string }) { return ( - + Partial diff - + {props.notice} @@ -69,7 +70,7 @@ function ReviewSelectionActionBar(props: { tintColor="#ffffff" type="monochrome" /> - {props.title} + {props.title} ); @@ -218,8 +219,8 @@ export function ReviewSheet() { if (error) { children.push( - Review unavailable - {error} + Review unavailable + {error} , ); } @@ -251,7 +252,7 @@ export function ReviewSheet() { numberOfLines={1} style={{ fontFamily: "DMSans_700Bold", - fontSize: 18, + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, fontWeight: "900", color: headerForeground, letterSpacing: -0.4, @@ -273,7 +274,7 @@ export function ReviewSheet() { - No review diffs - + No review diffs + This thread has no ready turn diffs and the worktree diff is empty. ) : selectedSection.isLoading && selectedSection.diff === null ? ( - Loading diff… + Loading diff… ) : parsedDiff.kind === "empty" ? ( - No changes - + No changes + {selectedSection.subtitle ?? "This diff is empty."} ) : parsedDiff.kind === "raw" ? ( - + {parsedDiff.reason} - + {parsedDiff.text} diff --git a/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts b/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts index d747dfc531b..f60fdfe70e0 100644 --- a/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts +++ b/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts @@ -5,6 +5,7 @@ import type { } from "../diffs/nativeReviewDiffTypes"; import * as Arr from "effect/Array"; import { pipe } from "effect/Function"; +import { MOBILE_CODE_SURFACE } from "../../lib/typography"; import { getPierreTerminalTheme, type TerminalAppearanceScheme } from "../terminal/terminalTheme"; import { computeWordAltDiffRanges } from "./reviewWordDiffs"; import { @@ -18,16 +19,16 @@ import type { ReviewInlineComment } from "./reviewCommentSelection"; const NATIVE_REVIEW_MAX_WORD_DIFF_RANGE_COUNT = 4; const NATIVE_REVIEW_MAX_WORD_DIFF_COVERAGE = 0.45; -export const NATIVE_REVIEW_DIFF_ROW_HEIGHT = 20; +export const NATIVE_REVIEW_DIFF_ROW_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; export const NATIVE_REVIEW_DIFF_CONTENT_WIDTH = 2_800; export const NATIVE_REVIEW_DIFF_STYLE = { rowHeight: NATIVE_REVIEW_DIFF_ROW_HEIGHT, contentWidth: NATIVE_REVIEW_DIFF_CONTENT_WIDTH, changeBarWidth: 4, - gutterWidth: 46, - codePadding: 7, - textVerticalInset: 2, + gutterWidth: MOBILE_CODE_SURFACE.gutterWidth, + codePadding: MOBILE_CODE_SURFACE.codePadding, + textVerticalInset: MOBILE_CODE_SURFACE.textVerticalInset, fileHeaderHeight: 56, fileHeaderHorizontalMargin: 8, fileHeaderVerticalMargin: 6, @@ -36,9 +37,9 @@ export const NATIVE_REVIEW_DIFF_STYLE = { fileHeaderPathRightPadding: 118, fileHeaderCountColumnWidth: 38, fileHeaderCountGap: 5, - codeFontSize: 11, + codeFontSize: MOBILE_CODE_SURFACE.fontSize, codeFontWeight: "regular", - lineNumberFontSize: 10, + lineNumberFontSize: MOBILE_CODE_SURFACE.lineNumberFontSize, lineNumberFontWeight: "regular", hunkFontSize: 11, hunkFontWeight: "medium", diff --git a/apps/mobile/src/features/review/reviewDiffRendering.tsx b/apps/mobile/src/features/review/reviewDiffRendering.tsx index 3f2ae01609e..14ff0276657 100644 --- a/apps/mobile/src/features/review/reviewDiffRendering.tsx +++ b/apps/mobile/src/features/review/reviewDiffRendering.tsx @@ -1,6 +1,7 @@ import { Platform, Text as NativeText, View } from "react-native"; import { cn } from "../../lib/cn"; +import { MOBILE_CODE_SURFACE } from "../../lib/typography"; import type { ReviewRenderableLineRow } from "./reviewModel"; import type { ReviewHighlightedToken } from "./shikiReviewHighlighter"; @@ -11,7 +12,7 @@ export const REVIEW_MONO_FONT_FAMILY = Platform.select({ default: "monospace", }); -export const REVIEW_DIFF_LINE_HEIGHT = 26; +export const REVIEW_DIFF_LINE_HEIGHT = MOBILE_CODE_SURFACE.rowHeight; const REVIEW_DELETE_STRIPE_COUNT = REVIEW_DIFF_LINE_HEIGHT / 2; export function renderVisibleWhitespace(value: string): string { @@ -71,8 +72,12 @@ export function DiffTokenText(props: { {renderVisibleWhitespace(props.fallback || " ")} @@ -83,8 +88,12 @@ export function DiffTokenText(props: { {(() => { let offset = 0; diff --git a/apps/mobile/src/features/terminal/NativeTerminalSurface.tsx b/apps/mobile/src/features/terminal/NativeTerminalSurface.tsx index 9d846a5fff2..ad693dcb445 100644 --- a/apps/mobile/src/features/terminal/NativeTerminalSurface.tsx +++ b/apps/mobile/src/features/terminal/NativeTerminalSurface.tsx @@ -11,6 +11,7 @@ import { } from "react-native"; import { AppText as Text } from "../../components/AppText"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { resolveNativeTerminalSurfaceView } from "./nativeTerminalModule"; import { buildGhosttyThemeConfig, @@ -53,7 +54,7 @@ function estimateGridSize(input: { } const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: TerminalSurfaceProps) { - const fontSize = props.fontSize ?? 12; + const fontSize = props.fontSize ?? MOBILE_TYPOGRAPHY.label.fontSize; const inputRef = useRef(null); const appearanceScheme = useColorScheme() === "light" ? "light" : "dark"; const theme = props.theme ?? getPierreTerminalTheme(appearanceScheme); @@ -93,7 +94,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter @@ -140,7 +141,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter color: theme.foreground, flex: 1, fontFamily: "Menlo", - fontSize: 13, + fontSize: MOBILE_TYPOGRAPHY.footnote.fontSize, padding: 0, }} onSubmitEditing={(event) => { @@ -165,7 +166,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter style={{ color: theme.foreground, fontFamily: "DMSans_700Bold", - fontSize: 11, + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, }} > Ctrl-C @@ -177,7 +178,7 @@ const FallbackTerminalSurface = memo(function FallbackTerminalSurface(props: Ter }); export const TerminalSurface = memo(function TerminalSurface(props: TerminalSurfaceProps) { - const fontSize = props.fontSize ?? 12; + const fontSize = props.fontSize ?? MOBILE_TYPOGRAPHY.label.fontSize; const keyboardInputRef = useRef(null); const appearanceScheme = useColorScheme() === "light" ? "light" : "dark"; const theme = props.theme ?? getPierreTerminalTheme(appearanceScheme); diff --git a/apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx b/apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx index b29a72c54b4..5d0b0547e6e 100644 --- a/apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx +++ b/apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx @@ -127,16 +127,16 @@ export const ThreadTerminalPanel = memo(function ThreadTerminalPanel( - + Terminal - + {nativeTerminalAvailable ? "Native Ghostty surface" : "Text fallback active"} {terminal.error ? ( - + {terminal.error} ) : null} diff --git a/apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx b/apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx index b0361f05575..8e9a47a58b5 100644 --- a/apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx +++ b/apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx @@ -25,6 +25,7 @@ import { terminalEnvironment } from "../../state/terminal"; import { useAtomCommand } from "../../state/use-atom-command"; import { useWorkspaceState } from "../../state/workspace"; import { buildThreadTerminalNavigation } from "../../lib/routes"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { useAttachedTerminalSession, useKnownTerminalSessions, @@ -920,7 +921,7 @@ export function ThreadTerminalRouteScreen() { style={{ color: terminalTheme.foreground, fontFamily: "DMSans_700Bold", - fontSize: 13, + fontSize: MOBILE_TYPOGRAPHY.footnote.fontSize, lineHeight: 16, }} > @@ -932,7 +933,7 @@ export function ThreadTerminalRouteScreen() { style={{ color: terminalTheme.mutedForeground, fontFamily: "Menlo", - fontSize: 11, + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, lineHeight: 14, }} > diff --git a/apps/mobile/src/features/threads/ComposerCommandPopover.tsx b/apps/mobile/src/features/threads/ComposerCommandPopover.tsx index 1b652a139cf..8b6fe078088 100644 --- a/apps/mobile/src/features/threads/ComposerCommandPopover.tsx +++ b/apps/mobile/src/features/threads/ComposerCommandPopover.tsx @@ -7,6 +7,7 @@ import { Pressable, ScrollView, useColorScheme, View, type ViewStyle } from "rea import { AppText as Text } from "../../components/AppText"; import { PierreEntryIcon } from "../../components/PierreEntryIcon"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; export type ComposerCommandItem = | { @@ -156,14 +157,22 @@ const CommandRow = memo(function CommandRow(props: { ) : null} {props.item.label} {props.item.description ? ( - + {props.item.description} ) : null} @@ -182,7 +191,7 @@ export const ComposerCommandPopover = memo(function ComposerCommandPopover( {label ? ( {label} @@ -206,7 +215,7 @@ export const ComposerCommandPopover = memo(function ComposerCommandPopover( ) : ( - + {emptyText(props.triggerKind, props.isLoading)} diff --git a/apps/mobile/src/features/threads/GitActionProgressOverlay.tsx b/apps/mobile/src/features/threads/GitActionProgressOverlay.tsx index 96fce3e3ccd..bda966cf16e 100644 --- a/apps/mobile/src/features/threads/GitActionProgressOverlay.tsx +++ b/apps/mobile/src/features/threads/GitActionProgressOverlay.tsx @@ -73,12 +73,12 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) { {progress.label ? ( - + {progress.label} ) : null} {progress.description ? ( - + {progress.description} ) : null} diff --git a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx index 8a93989f3d9..f8596e3e51e 100644 --- a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx +++ b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx @@ -30,6 +30,7 @@ import { resolveProviderOptionDescriptors, } from "../../lib/providerOptions"; import { buildThreadRoutePath } from "../../lib/routes"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { useProjects } from "../../state/entities"; import { branchBadgeLabel, useNewTaskFlow } from "./new-task-flow-provider"; import { useCreateProjectThread } from "./use-project-actions"; @@ -430,7 +431,10 @@ export function NewTaskDraftScreen(props: { onPasteImages={(uris) => void handleNativePasteImages(uris)} placeholder={`Describe a coding task in ${selectedProject.title}`} style={{ flex: 1, minHeight: 0 }} - textStyle={{ fontSize: 18, lineHeight: 28 }} + textStyle={{ + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, + lineHeight: MOBILE_TYPOGRAPHY.title.lineHeight, + }} /> diff --git a/apps/mobile/src/features/threads/PendingApprovalCard.tsx b/apps/mobile/src/features/threads/PendingApprovalCard.tsx index 79a01cdada3..0617ef1cbcf 100644 --- a/apps/mobile/src/features/threads/PendingApprovalCard.tsx +++ b/apps/mobile/src/features/threads/PendingApprovalCard.tsx @@ -16,7 +16,7 @@ export interface PendingApprovalCardProps { export function PendingApprovalCard(props: PendingApprovalCardProps) { return ( - + Approval needed diff --git a/apps/mobile/src/features/threads/PendingUserInputCard.tsx b/apps/mobile/src/features/threads/PendingUserInputCard.tsx index 5bd0d4e4a3c..c9e01777214 100644 --- a/apps/mobile/src/features/threads/PendingUserInputCard.tsx +++ b/apps/mobile/src/features/threads/PendingUserInputCard.tsx @@ -26,7 +26,7 @@ export interface PendingUserInputCardProps { export function PendingUserInputCard(props: PendingUserInputCardProps) { return ( - + User input needed @@ -39,7 +39,7 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) { {question.header} - + {question.question} @@ -65,7 +65,7 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) { > ); diff --git a/apps/mobile/src/features/threads/ThreadComposer.tsx b/apps/mobile/src/features/threads/ThreadComposer.tsx index edac061daec..bd72aa0b1a4 100644 --- a/apps/mobile/src/features/threads/ThreadComposer.tsx +++ b/apps/mobile/src/features/threads/ThreadComposer.tsx @@ -43,6 +43,7 @@ import { ControlPill, ControlPillMenu } from "../../components/ControlPill"; import { ProviderIcon } from "../../components/ProviderIcon"; import type { DraftComposerImageAttachment } from "../../lib/composerImages"; import { buildModelOptions, groupByProvider } from "../../lib/modelOptions"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import type { RemoteClientConnectionState } from "../../lib/connection"; import { insertRankedSearchResult, @@ -189,7 +190,7 @@ const ComposerConnectionStatusPill = memo(function ComposerConnectionStatusPill( )} {props.status.label} @@ -717,7 +718,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer } } textStyle={{ - fontSize: 15, + fontSize: MOBILE_TYPOGRAPHY.body.fontSize, lineHeight: isExpanded ? 22 : 20, color: foregroundColor, fontFamily: "DMSans_400Regular", @@ -751,7 +752,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer justifyContent: "center", }} > - + +{props.draftAttachments.length - 3} @@ -831,8 +832,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx index 55863557139..7424d856368 100644 --- a/apps/mobile/src/features/threads/ThreadFeed.tsx +++ b/apps/mobile/src/features/threads/ThreadFeed.tsx @@ -56,6 +56,7 @@ import { buildReviewParsedDiff } from "../review/reviewModel"; import { cn } from "../../lib/cn"; import type { LayoutVariant } from "../../lib/layout"; import { buildThreadFilesNavigation } from "../../lib/routes"; +import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { markdownFileIconSource } from "@t3tools/mobile-markdown-text/file-icons"; import { resolveMarkdownLinkPresentation } from "@t3tools/mobile-markdown-text/links"; import { @@ -444,8 +445,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe marginRight: 5, color: inlineTextColor, fontFamily: "DMSans_400Regular", - fontSize: 15, - lineHeight: 22, + ...MOBILE_TYPOGRAPHY.body, textAlign: ordered ? "right" : "center", }} > @@ -466,7 +466,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe style={{ color: inlineCodeTextColor, fontFamily: "ui-monospace", - fontSize: 12, + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, lineHeight: 22, }} > @@ -503,7 +503,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe style={{ color: markdownBodyColor, fontFamily: "ui-monospace", - fontSize: 12, + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, opacity: 0.7, textTransform: "uppercase", }} @@ -523,7 +523,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe style={{ color: blockTextColor, fontFamily: "ui-monospace", - fontSize: 12, + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, lineHeight: 18, }} > @@ -603,8 +603,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe skillTextColor: "#f0abfc", quoteMarkerColor: markdownUserBodyColor, dividerColor: markdownUserBodyColor, - fontSize: 15, - lineHeight: 22, + ...MOBILE_TYPOGRAPHY.body, fontFamily: "DMSans_400Regular", headingFontFamily: "DMSans_700Bold", boldFontFamily: "DMSans_700Bold", @@ -633,8 +632,7 @@ function useMarkdownStyles(onLinkPress: (href: string) => void): MarkdownStyleSe skillTextColor: inlineSkillForeground, quoteMarkerColor: markdownBlockquoteBorder, dividerColor: markdownHrColor, - fontSize: 15, - lineHeight: 22, + ...MOBILE_TYPOGRAPHY.body, fontFamily: "DMSans_400Regular", headingFontFamily: "DMSans_700Bold", boldFontFamily: "DMSans_700Bold", @@ -821,7 +819,7 @@ function renderFeedEntry( className="max-w-[85%] gap-2 rounded-[22px] rounded-br-[6px] px-3.5 py-2.5 opacity-60" style={{ backgroundColor: userBubbleColor }} > - + {entry.queuedMessage.text} {entry.queuedMessage.attachments.length > 0 ? ( @@ -995,7 +993,7 @@ const ReviewCommentCard = memo(function ReviewCommentCard(props: { @@ -1040,8 +1038,8 @@ const ReviewCommentCard = memo(function ReviewCommentCard(props: { style={{ color: props.colors.text, fontFamily: "ui-monospace", - fontSize: 12, - lineHeight: 18, + fontSize: MOBILE_CODE_SURFACE.fontSize, + lineHeight: MOBILE_CODE_SURFACE.rowHeight, }} > {props.comment.diff.trim()} @@ -1052,7 +1050,7 @@ const ReviewCommentCard = memo(function ReviewCommentCard(props: { {props.comment.text} diff --git a/apps/mobile/src/features/threads/ThreadNavigationDrawer.tsx b/apps/mobile/src/features/threads/ThreadNavigationDrawer.tsx index 77a80fff550..9318fb76017 100644 --- a/apps/mobile/src/features/threads/ThreadNavigationDrawer.tsx +++ b/apps/mobile/src/features/threads/ThreadNavigationDrawer.tsx @@ -152,7 +152,7 @@ export function ThreadNavigationDrawer(props: { ]} > - Threads + Threads { props.onClose(); @@ -224,7 +224,7 @@ function ThreadNavigationDrawerContent(props: { {groupedThreads.map((group) => ( {group.title} @@ -233,9 +233,7 @@ function ThreadNavigationDrawerContent(props: { {group.threads.length === 0 ? ( - - No threads yet - + No threads yet ) : ( group.threads.map((thread, index) => { @@ -260,11 +258,11 @@ function ThreadNavigationDrawerContent(props: { > - + {thread.title} {relativeTime(thread.updatedAt ?? thread.createdAt)} diff --git a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx index 51f5832f50f..7bb74ae88ff 100644 --- a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx +++ b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx @@ -20,6 +20,7 @@ import { EmptyState } from "../../components/EmptyState"; import { LoadingScreen } from "../../components/LoadingScreen"; import { buildThreadRoutePath, buildThreadTerminalNavigation } from "../../lib/routes"; import { scopedThreadKey } from "../../lib/scopedEntities"; +import { MOBILE_TYPOGRAPHY } from "../../lib/typography"; import { connectionTone } from "../connection/connectionTone"; import { @@ -390,7 +391,7 @@ export function ThreadRouteScreen() { numberOfLines={1} style={{ fontFamily: "DMSans_700Bold", - fontSize: 18, + fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, fontWeight: "900", color: foregroundColor, letterSpacing: -0.4, @@ -402,7 +403,7 @@ export function ThreadRouteScreen() { numberOfLines={1} style={{ fontFamily: "DMSans_700Bold", - fontSize: 12, + fontSize: MOBILE_TYPOGRAPHY.label.fontSize, fontWeight: "700", color: secondaryFg, letterSpacing: 0.3, diff --git a/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx b/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx index 3fbea89ba32..e27136702f2 100644 --- a/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx +++ b/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx @@ -69,7 +69,7 @@ export function GitBranchesSheet() { > New branch @@ -78,7 +78,7 @@ export function GitBranchesSheet() { value={newBranchName} onChangeText={setNewBranchName} placeholder="feature/mobile-polish" - className="rounded-[18px] px-3.5 py-3 font-sans text-[15px]" + className="rounded-[18px] px-3.5 py-3 font-sans text-base" style={{ borderWidth: 1, borderColor: inputBorderColor, @@ -104,7 +104,7 @@ export function GitBranchesSheet() { New worktree @@ -113,7 +113,7 @@ export function GitBranchesSheet() { value={worktreeBaseBranch} onChangeText={setWorktreeBaseBranch} placeholder="main" - className="rounded-[18px] px-3.5 py-3 font-sans text-[15px]" + className="rounded-[18px] px-3.5 py-3 font-sans text-base" style={{ borderWidth: 1, borderColor: inputBorderColor, @@ -125,7 +125,7 @@ export function GitBranchesSheet() { value={worktreeBranchName} onChangeText={setWorktreeBranchName} placeholder="feature/mobile-thread" - className="rounded-[18px] px-3.5 py-3 font-sans text-[15px]" + className="rounded-[18px] px-3.5 py-3 font-sans text-base" style={{ borderWidth: 1, borderColor: inputBorderColor, @@ -154,18 +154,16 @@ export function GitBranchesSheet() { Existing branches {branchesLoading ? ( - - Loading branches... - + Loading branches... ) : null} {!branchesLoading && availableBranches.length === 0 ? ( - + No local branches found. ) : null} @@ -195,8 +193,8 @@ export function GitBranchesSheet() { }} > - {branch.name} - {subtitle} + {branch.name} + {subtitle} ); })} diff --git a/apps/mobile/src/features/threads/git/GitCommitSheet.tsx b/apps/mobile/src/features/threads/git/GitCommitSheet.tsx index 9e20f5b1560..76e0daf5f0a 100644 --- a/apps/mobile/src/features/threads/git/GitCommitSheet.tsx +++ b/apps/mobile/src/features/threads/git/GitCommitSheet.tsx @@ -79,14 +79,14 @@ export function GitCommitSheet() { > - Branch - + Branch + {gitStatus.data?.refName ?? "(detached HEAD)"} {isDefaultRef ? ( Warning: this is the default branch. @@ -97,8 +97,8 @@ export function GitCommitSheet() { - Files - + Files + {selectedFiles.length} selected · +{selectedInsertions} / -{selectedDeletions} @@ -108,14 +108,14 @@ export function GitCommitSheet() { className="bg-subtle rounded-full px-3 py-2" onPress={() => setExcludedFiles(new Set())} > - Reset + Reset ) : null} setIsEditingFiles((current) => !current)} > - + {isEditingFiles ? "Done" : "Edit"} @@ -123,26 +123,26 @@ export function GitCommitSheet() { {allFiles.length === 0 ? ( - + No changed files are available to commit. ) : !isEditingFiles ? ( {selectedFilePreview.map((file) => ( - + {file.path} - + +{file.insertions} - + -{file.deletions} ))} {selectedFiles.length > selectedFilePreview.length ? ( - + +{selectedFiles.length - selectedFilePreview.length} more files ) : null} @@ -177,21 +177,21 @@ export function GitCommitSheet() { {file.path} {!included ? ( - + Excluded from this commit ) : null} - + +{file.insertions} - + -{file.deletions} @@ -204,14 +204,14 @@ export function GitCommitSheet() { - Commit message + Commit message Confirm - + {copy?.title ?? "Run action on default branch?"} - + {copy?.description ?? "Choose how to continue."} diff --git a/apps/mobile/src/features/threads/git/GitOverviewSheet.tsx b/apps/mobile/src/features/threads/git/GitOverviewSheet.tsx index 314d0cfcd20..d6255a296b7 100644 --- a/apps/mobile/src/features/threads/git/GitOverviewSheet.tsx +++ b/apps/mobile/src/features/threads/git/GitOverviewSheet.tsx @@ -175,13 +175,13 @@ export function GitOverviewSheet() { /> Branch - {currentBranchLabel} - + {currentBranchLabel} + {statusSummary(gitStatus.data)} diff --git a/apps/mobile/src/features/threads/git/gitSheetComponents.tsx b/apps/mobile/src/features/threads/git/gitSheetComponents.tsx index b13f6a3020c..16c311bff57 100644 --- a/apps/mobile/src/features/threads/git/gitSheetComponents.tsx +++ b/apps/mobile/src/features/threads/git/gitSheetComponents.tsx @@ -56,7 +56,7 @@ export function SheetActionButton(props: { > {props.label} @@ -69,12 +69,12 @@ export function MetaCard(props: { readonly label: string; readonly value: string return ( {props.label} - + {props.value} @@ -102,9 +102,9 @@ export function SheetListRow(props: { - {props.title} + {props.title} {props.subtitle ? ( - {props.subtitle} + {props.subtitle} ) : null} diff --git a/apps/mobile/src/features/threads/thread-work-log.tsx b/apps/mobile/src/features/threads/thread-work-log.tsx index 244998eb336..707e1a24f0d 100644 --- a/apps/mobile/src/features/threads/thread-work-log.tsx +++ b/apps/mobile/src/features/threads/thread-work-log.tsx @@ -98,7 +98,7 @@ export function ThreadWorkLog(props: { return ( {!onlyToolRows ? ( - + work log ) : null} @@ -164,7 +164,7 @@ export function ThreadWorkLog(props: { {props.copiedRowId === row.id ? ( - + Copied ) : null} @@ -209,7 +209,7 @@ export function ThreadWorkLog(props: { > {row.fullDetail} diff --git a/apps/mobile/src/lib/typography.test.ts b/apps/mobile/src/lib/typography.test.ts new file mode 100644 index 00000000000..ac34e1b9dc0 --- /dev/null +++ b/apps/mobile/src/lib/typography.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "./typography"; + +describe("mobile typography", () => { + it("uses the intentional compact mobile font scale", () => { + expect(Object.values(MOBILE_TYPOGRAPHY).map(({ fontSize }) => fontSize)).toEqual([ + 10, 11, 12, 13, 15, 17, 20, 24, 28, + ]); + }); + + it("uses caption-sized code with a compact readable row height", () => { + expect(MOBILE_CODE_SURFACE).toMatchObject({ + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, + lineNumberFontSize: MOBILE_TYPOGRAPHY.micro.fontSize, + rowHeight: 20, + }); + }); +}); diff --git a/apps/mobile/src/lib/typography.ts b/apps/mobile/src/lib/typography.ts new file mode 100644 index 00000000000..671c09a75f1 --- /dev/null +++ b/apps/mobile/src/lib/typography.ts @@ -0,0 +1,21 @@ +export const MOBILE_TYPOGRAPHY = { + micro: { fontSize: 10, lineHeight: 13 }, + caption: { fontSize: 11, lineHeight: 15 }, + label: { fontSize: 12, lineHeight: 16 }, + footnote: { fontSize: 13, lineHeight: 18 }, + body: { fontSize: 15, lineHeight: 22 }, + headline: { fontSize: 17, lineHeight: 22 }, + title: { fontSize: 20, lineHeight: 26 }, + largeTitle: { fontSize: 24, lineHeight: 30 }, + display: { fontSize: 28, lineHeight: 34 }, +} as const; + +/** Shared geometry for dense, horizontally scrolling code surfaces. */ +export const MOBILE_CODE_SURFACE = { + rowHeight: 20, + gutterWidth: 46, + codePadding: 7, + textVerticalInset: 2, + fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, + lineNumberFontSize: MOBILE_TYPOGRAPHY.micro.fontSize, +} as const; diff --git a/apps/mobile/src/native/T3ComposerEditor.tsx b/apps/mobile/src/native/T3ComposerEditor.tsx index 0f20e9e042d..f26780562f3 100644 --- a/apps/mobile/src/native/T3ComposerEditor.tsx +++ b/apps/mobile/src/native/T3ComposerEditor.tsx @@ -2,6 +2,7 @@ import { TextInputWrapper } from "expo-paste-input"; import { useImperativeHandle, useRef } from "react"; import { TextInput, type TextInput as RNTextInput } from "react-native"; +import { MOBILE_TYPOGRAPHY } from "../lib/typography"; import { useThemeColor } from "../lib/useThemeColor"; import { useNativePaste } from "../lib/useNativePaste"; import type { ComposerEditorProps } from "./T3ComposerEditor.types"; @@ -47,8 +48,7 @@ export function ComposerEditor({ minHeight: 0, color: foregroundColor, fontFamily: "DMSans_400Regular", - fontSize: 15, - lineHeight: 22, + ...MOBILE_TYPOGRAPHY.body, paddingVertical: contentInsetVertical, }, textStyle, From 6e25eb971e47b6966e5c16ac725a7302c6f021c2 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 19 Jun 2026 15:58:00 -0700 Subject: [PATCH 2/3] Unify composer typography across mobile editors - Add a shared compact composer text style - Apply it in thread composers and the iOS editor - Restore base typing attributes on iOS input and keep lint coverage --- apps/mobile/.swiftlint.yml | 1 + .../ios/T3ComposerEditorView.swift | 31 ++++++++++++++++--- .../features/threads/NewTaskDraftScreen.tsx | 5 +-- .../src/features/threads/ThreadComposer.tsx | 3 +- apps/mobile/src/lib/typography.test.ts | 6 +++- apps/mobile/src/lib/typography.ts | 1 + .../src/native/T3ComposerEditor.ios.tsx | 11 +++++-- apps/mobile/src/native/T3ComposerEditor.tsx | 2 +- 8 files changed, 46 insertions(+), 14 deletions(-) diff --git a/apps/mobile/.swiftlint.yml b/apps/mobile/.swiftlint.yml index 83fc429b731..0714ce90e63 100644 --- a/apps/mobile/.swiftlint.yml +++ b/apps/mobile/.swiftlint.yml @@ -1,5 +1,6 @@ included: - ios/T3Code + - modules/t3-composer-editor/ios - modules/t3-terminal/ios - modules/t3-review-diff/ios diff --git a/apps/mobile/modules/t3-composer-editor/ios/T3ComposerEditorView.swift b/apps/mobile/modules/t3-composer-editor/ios/T3ComposerEditorView.swift index a88acbc31f7..6f4dc575b12 100644 --- a/apps/mobile/modules/t3-composer-editor/ios/T3ComposerEditorView.swift +++ b/apps/mobile/modules/t3-composer-editor/ios/T3ComposerEditorView.swift @@ -275,8 +275,8 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { fileTint: "#737373" ) private var fontFamily = "DMSans_400Regular" - private var fontSize: CGFloat = 15 - private var lineHeight: CGFloat = 22 + private var fontSize: CGFloat = 14 + private var lineHeight: CGFloat = 20 private var contentInsetVertical: CGFloat = 0 private var shouldAutoFocus = false private var didAutoFocus = false @@ -460,9 +460,19 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { guard !isApplyingControlledValue else { return } + restoreBaseTypingAttributes() emitSelection() } + public func textView( + _ textView: UITextView, + shouldChangeTextIn range: NSRange, + replacementText text: String + ) -> Bool { + restoreBaseTypingAttributes() + return true + } + public func textViewDidBeginEditing(_ textView: UITextView) { onComposerFocus() } @@ -484,6 +494,7 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { let targetSelection = requestedSelection ?? previousSelection requestedSelection = nil textView.selectedRange = displayRange(for: targetSelection) + restoreBaseTypingAttributes() isApplyingControlledValue = false updatePlaceholderVisibility() emitContentSizeIfNeeded() @@ -556,7 +567,12 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { size: image.size, baselineOffset: baselineOffset ) - return NSAttributedString(attachment: attachment) + let attributedAttachment = NSMutableAttributedString(attachment: attachment) + attributedAttachment.addAttributes( + baseAttributes(), + range: NSRange(location: 0, length: attributedAttachment.length) + ) + return attributedAttachment } private func renderChip( @@ -660,11 +676,18 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate { let font = UIFont(name: fontFamily, size: fontSize) ?? UIFont.systemFont(ofSize: fontSize) textView.font = font - textView.typingAttributes = baseAttributes() + restoreBaseTypingAttributes() placeholderLabel.font = font setNeedsLayout() } + private func restoreBaseTypingAttributes() { + guard textView.markedTextRange == nil else { + return + } + textView.typingAttributes = baseAttributes() + } + private func applyTheme() { textView.textColor = UIColor(composerHex: theme.text) ?? .label placeholderLabel.textColor = UIColor(composerHex: theme.placeholder) ?? .placeholderText diff --git a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx index f8596e3e51e..92c13c20070 100644 --- a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx +++ b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx @@ -431,10 +431,7 @@ export function NewTaskDraftScreen(props: { onPasteImages={(uris) => void handleNativePasteImages(uris)} placeholder={`Describe a coding task in ${selectedProject.title}`} style={{ flex: 1, minHeight: 0 }} - textStyle={{ - fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, - lineHeight: MOBILE_TYPOGRAPHY.title.lineHeight, - }} + textStyle={MOBILE_TYPOGRAPHY.composer} /> diff --git a/apps/mobile/src/features/threads/ThreadComposer.tsx b/apps/mobile/src/features/threads/ThreadComposer.tsx index bd72aa0b1a4..0050eb923be 100644 --- a/apps/mobile/src/features/threads/ThreadComposer.tsx +++ b/apps/mobile/src/features/threads/ThreadComposer.tsx @@ -718,8 +718,7 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer } } textStyle={{ - fontSize: MOBILE_TYPOGRAPHY.body.fontSize, - lineHeight: isExpanded ? 22 : 20, + ...MOBILE_TYPOGRAPHY.composer, color: foregroundColor, fontFamily: "DMSans_400Regular", }} diff --git a/apps/mobile/src/lib/typography.test.ts b/apps/mobile/src/lib/typography.test.ts index ac34e1b9dc0..6a021dabcce 100644 --- a/apps/mobile/src/lib/typography.test.ts +++ b/apps/mobile/src/lib/typography.test.ts @@ -5,10 +5,14 @@ import { MOBILE_CODE_SURFACE, MOBILE_TYPOGRAPHY } from "./typography"; describe("mobile typography", () => { it("uses the intentional compact mobile font scale", () => { expect(Object.values(MOBILE_TYPOGRAPHY).map(({ fontSize }) => fontSize)).toEqual([ - 10, 11, 12, 13, 15, 17, 20, 24, 28, + 10, 11, 12, 13, 14, 15, 17, 20, 24, 28, ]); }); + it("uses a compact shared style for editable composer text", () => { + expect(MOBILE_TYPOGRAPHY.composer).toEqual({ fontSize: 14, lineHeight: 20 }); + }); + it("uses caption-sized code with a compact readable row height", () => { expect(MOBILE_CODE_SURFACE).toMatchObject({ fontSize: MOBILE_TYPOGRAPHY.caption.fontSize, diff --git a/apps/mobile/src/lib/typography.ts b/apps/mobile/src/lib/typography.ts index 671c09a75f1..644fee36589 100644 --- a/apps/mobile/src/lib/typography.ts +++ b/apps/mobile/src/lib/typography.ts @@ -3,6 +3,7 @@ export const MOBILE_TYPOGRAPHY = { caption: { fontSize: 11, lineHeight: 15 }, label: { fontSize: 12, lineHeight: 16 }, footnote: { fontSize: 13, lineHeight: 18 }, + composer: { fontSize: 14, lineHeight: 20 }, body: { fontSize: 15, lineHeight: 22 }, headline: { fontSize: 17, lineHeight: 22 }, title: { fontSize: 20, lineHeight: 26 }, diff --git a/apps/mobile/src/native/T3ComposerEditor.ios.tsx b/apps/mobile/src/native/T3ComposerEditor.ios.tsx index 6778b0455d5..7dd92ff067f 100644 --- a/apps/mobile/src/native/T3ComposerEditor.ios.tsx +++ b/apps/mobile/src/native/T3ComposerEditor.ios.tsx @@ -6,6 +6,7 @@ import { Image, StyleSheet } from "react-native"; import { markdownFileIconSource } from "@t3tools/mobile-markdown-text/file-icons"; import { resolveMarkdownFileIcon } from "@t3tools/mobile-markdown-text/links"; +import { MOBILE_TYPOGRAPHY } from "../lib/typography"; import { useThemeColor } from "../lib/useThemeColor"; import type { ComposerEditorProps, ComposerEditorSelection } from "./T3ComposerEditor.types"; @@ -150,9 +151,15 @@ export function ComposerEditor({ ? resolvedTextStyle.fontFamily : "DMSans_400Regular" } - fontSize={typeof resolvedTextStyle.fontSize === "number" ? resolvedTextStyle.fontSize : 15} + fontSize={ + typeof resolvedTextStyle.fontSize === "number" + ? resolvedTextStyle.fontSize + : MOBILE_TYPOGRAPHY.composer.fontSize + } lineHeight={ - typeof resolvedTextStyle.lineHeight === "number" ? resolvedTextStyle.lineHeight : 22 + typeof resolvedTextStyle.lineHeight === "number" + ? resolvedTextStyle.lineHeight + : MOBILE_TYPOGRAPHY.composer.lineHeight } contentInsetVertical={contentInsetVertical} editable={props.editable ?? true} diff --git a/apps/mobile/src/native/T3ComposerEditor.tsx b/apps/mobile/src/native/T3ComposerEditor.tsx index f26780562f3..dc2dfdfee03 100644 --- a/apps/mobile/src/native/T3ComposerEditor.tsx +++ b/apps/mobile/src/native/T3ComposerEditor.tsx @@ -48,7 +48,7 @@ export function ComposerEditor({ minHeight: 0, color: foregroundColor, fontFamily: "DMSans_400Regular", - ...MOBILE_TYPOGRAPHY.body, + ...MOBILE_TYPOGRAPHY.composer, paddingVertical: contentInsetVertical, }, textStyle, From 124e471ff04f78e320d37e7c1356a7e6c9dccb6c Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 19 Jun 2026 15:59:55 -0700 Subject: [PATCH 3/3] Increase archive error heading size - Bump the archive load error heading from small to base text for better emphasis - Keep the retry message and layout unchanged --- apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx b/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx index fa89784c86d..3e1934100cd 100644 --- a/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx +++ b/apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx @@ -314,7 +314,7 @@ function ArchivedThreadRow(props: { function ArchiveError(props: { readonly message: string; readonly onRetry: () => void }) { return ( - + Could not load every archive {props.message}