diff --git a/apps/mobile/src/screens/form-request-card.test.tsx b/apps/mobile/src/screens/form-request-card.test.tsx
index 4f88d27..9f6657b 100644
--- a/apps/mobile/src/screens/form-request-card.test.tsx
+++ b/apps/mobile/src/screens/form-request-card.test.tsx
@@ -3,6 +3,7 @@ import type { FormInfo } from "@opencode2-mobile/opencode-adapter";
import { fireEvent, render, screen } from "@testing-library/react-native";
import { Alert, Linking } from "react-native";
+import { palette, typography } from "../theme";
import { FormRequestCard, openExternalFormUrl } from "./form-request-card";
const form: FormInfo = {
@@ -29,6 +30,17 @@ const form: FormInfo = {
title: "Deploy",
};
+test("question controls use shared typography and neutral attention colors", () => {
+ render();
+
+ expect(screen.getByText("Input required")).toHaveStyle({
+ ...typography.label,
+ color: palette.dim,
+ });
+ expect(screen.getByText("Production")).toHaveStyle(typography.control);
+ expect(screen.getByLabelText("name")).toHaveStyle(typography.body);
+});
+
test("renders all field controls, conditional visibility, and a validated reply", () => {
const onSubmit = jest.fn();
render();
diff --git a/apps/mobile/src/screens/form-request-card.tsx b/apps/mobile/src/screens/form-request-card.tsx
index 01b2cf7..f12d97a 100644
--- a/apps/mobile/src/screens/form-request-card.tsx
+++ b/apps/mobile/src/screens/form-request-card.tsx
@@ -334,8 +334,17 @@ function ChoiceButton({
pressed && styles.pressed,
]}
>
- {label}
- {description ? {description} : null}
+
+ {label}
+
+ {description ? (
+
+ {description}
+
+ ) : null}
);
}
@@ -368,6 +377,7 @@ function FormButton({
]}
>
{
+ render();
+ expect(screen.getByText("Permission required")).toHaveStyle({
+ ...typography.heading,
+ color: palette.ink,
+ });
+ expect(screen.getByRole("alert")).toHaveStyle({
+ ...typography.body,
+ color: palette.danger,
+ });
+});
+
test("shell requests show a short preview and preserve full commands and scope in Details", () => {
const reply = jest.fn();
render(
diff --git a/apps/mobile/src/screens/permission-request-card.tsx b/apps/mobile/src/screens/permission-request-card.tsx
index 3440e13..deac83b 100644
--- a/apps/mobile/src/screens/permission-request-card.tsx
+++ b/apps/mobile/src/screens/permission-request-card.tsx
@@ -123,7 +123,7 @@ export function PermissionRequestCard({
) : null}
{error ? (
-
+
The server did not accept that reply. Review the refreshed request and try again.
) : null}
@@ -161,7 +161,7 @@ const styles = StyleSheet.create({
padding: 12,
gap: 8,
},
- title: { ...typography.heading, color: palette.warm },
+ title: { ...typography.heading, color: palette.ink },
action: { ...typography.body, color: palette.ink },
commandBox: { backgroundColor: palette.background, borderRadius: radius.sm },
command: {
@@ -190,6 +190,7 @@ const styles = StyleSheet.create({
primaryLabel: { color: palette.background },
buttonLabel: { ...typography.control, color: palette.ink, textAlign: "center" },
reject: { color: palette.danger },
+ error: { ...typography.body, color: palette.danger },
disabled: { opacity: 0.5 },
detailsButton: { minHeight: 44, justifyContent: "center" },
});
diff --git a/apps/mobile/src/screens/session-transcript.test.tsx b/apps/mobile/src/screens/session-transcript.test.tsx
index a852846..a4d3f55 100644
--- a/apps/mobile/src/screens/session-transcript.test.tsx
+++ b/apps/mobile/src/screens/session-transcript.test.tsx
@@ -5,7 +5,7 @@ import * as Clipboard from "expo-clipboard";
import { Alert, Linking, View } from "react-native";
import { resetTranscriptPerformanceMetrics } from "../state/transcript-performance";
-import { markdownPalette, palette } from "../theme";
+import { markdownPalette, palette, typography } from "../theme";
import {
activitySummary,
buildTranscriptPresentation,
@@ -17,6 +17,25 @@ import {
afterEach(resetTranscriptPerformanceMetrics);
+test("assistant prose uses the shared body typography and text color", async () => {
+ const message = messages.find((item) => item.type === "assistant");
+ if (!message) throw new Error("fixture");
+ render(
+ ,
+ );
+ let text = screen.getByText("Themed response");
+ while (!text.props.selectable && text.parent) text = text.parent;
+ expect(text).toHaveStyle({
+ ...typography.body,
+ color: palette.ink,
+ });
+ await waitFor(() =>
+ expect(screen.getByRole("button", { name: "Copy response" })).toBeOnTheScreen(),
+ );
+});
+
jest.mock("@opencode2-mobile/opencode-adapter", () => ({
getOpenCodeShell: jest.fn(),
getOpenCodeShellOutput: jest.fn(),
@@ -747,7 +766,7 @@ test("shows the responding model display name and measured runtime in the footer
}}
/>,
);
- expect(screen.getByText("Plan · Model One · 28s")).toHaveStyle({ fontSize: 14, lineHeight: 20 });
+ expect(screen.getByText("Plan · Model One · 28s")).toHaveStyle(typography.caption);
});
test("renders fenced assistant code without markdown fence markers", () => {
diff --git a/apps/mobile/src/screens/session-transcript.tsx b/apps/mobile/src/screens/session-transcript.tsx
index 73893c8..2c532e2 100644
--- a/apps/mobile/src/screens/session-transcript.tsx
+++ b/apps/mobile/src/screens/session-transcript.tsx
@@ -1809,7 +1809,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 0,
},
loadedSkill: { minHeight: 44, justifyContent: "center", paddingVertical: 4 },
- activityAction: { color: palette.dim, fontSize: 14 },
+ activityAction: { ...typography.control, color: palette.dim },
activityError: {
borderBottomWidth: 0,
marginHorizontal: -space.sm,
@@ -1827,7 +1827,7 @@ const styles = StyleSheet.create({
minWidth: 0,
},
activityCopyLargeText: { alignItems: "flex-start", flexDirection: "column", gap: 2 },
- activityDetail: { color: palette.dim, flexShrink: 1, fontSize: 14, lineHeight: 18 },
+ activityDetail: { ...typography.caption, color: palette.dim, flexShrink: 1 },
activityFile: {
...typography.code,
borderTopColor: palette.border,
@@ -1845,14 +1845,14 @@ const styles = StyleSheet.create({
paddingVertical: 8,
},
activityHeaderLargeText: { alignItems: "flex-start", flexDirection: "column" },
- activityLabel: { color: palette.ink, fontSize: 14, lineHeight: 20, fontWeight: "500" },
- activitySummary: { color: palette.dim, fontSize: 14, lineHeight: 20, flexShrink: 1 },
+ activityLabel: { ...typography.control, color: palette.ink },
+ activitySummary: { ...typography.caption, color: palette.dim, flexShrink: 1 },
activityNested: { marginLeft: 0 },
compactActivity: { paddingLeft: space.sm, gap: 2 },
toolDetails: { paddingLeft: 0 },
responseFooter: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", gap: space.xs },
activityStandalone: { marginHorizontal: space.md, paddingVertical: space.xs },
- assistantFooter: { color: palette.dim, fontSize: 14, lineHeight: 20, flexShrink: 1 },
+ assistantFooter: { ...typography.caption, color: palette.dim, flexShrink: 1 },
assistantRow: {
gap: space.xs,
paddingHorizontal: space.md,
@@ -1867,13 +1867,13 @@ const styles = StyleSheet.create({
paddingHorizontal: 9,
paddingVertical: 5,
},
- attachmentLabel: { color: palette.dim, fontSize: 11, fontWeight: "600" },
+ attachmentLabel: { ...typography.label, color: palette.dim },
attachments: { flexDirection: "row", flexWrap: "wrap", gap: space.xs, marginTop: space.sm },
- bodyText: { color: palette.ink, fontSize: 16, lineHeight: 23 },
+ bodyText: { ...typography.body, color: palette.ink },
disclosure: {
backgroundColor: "transparent",
},
- disclosureAction: { color: palette.dim, fontSize: 14 },
+ disclosureAction: { ...typography.control, color: palette.dim },
disclosureActionLargeText: { alignSelf: "flex-start" },
disclosureHeader: {
alignItems: "center",
@@ -1887,11 +1887,10 @@ const styles = StyleSheet.create({
},
disclosureHeaderLargeText: { alignItems: "flex-start", flexDirection: "column" },
disclosureLabel: {
+ ...typography.label,
color: palette.dim,
flex: 1,
flexShrink: 1,
- fontSize: 12,
- fontWeight: "500",
minWidth: 0,
},
disclosureLabelLargeText: { flex: 0, width: "100%" },
@@ -1901,16 +1900,16 @@ const styles = StyleSheet.create({
minHeight: 44,
paddingRight: space.md,
},
- diffActionLabel: { color: palette.signal, fontSize: 14, fontWeight: "700" },
- errorText: { color: palette.danger, fontSize: 14, lineHeight: 21 },
+ diffActionLabel: { ...typography.control, color: palette.signal },
+ errorText: { ...typography.body, color: palette.danger },
linkText: { color: markdownPalette.linkText, textDecorationLine: "underline" },
notice: {
marginHorizontal: space.md,
paddingVertical: space.xs,
},
noticeLabel: { ...typography.label, color: palette.dim },
- noticeText: { color: palette.dim, fontSize: 14, lineHeight: 19, marginTop: 5 },
- omittedText: { color: palette.dim, fontSize: 11, marginTop: 7 },
+ noticeText: { ...typography.body, color: palette.dim, marginTop: 5 },
+ omittedText: { ...typography.caption, color: palette.dim, marginTop: 7 },
outputText: {
...typography.code,
color: palette.dim,
@@ -1921,8 +1920,8 @@ const styles = StyleSheet.create({
...typography.label,
color: markdownPalette.reasoning,
},
- reasoningText: { color: palette.dim, fontSize: 14, lineHeight: 19 },
- statusText: { color: palette.dim, fontSize: 12 },
+ reasoningText: { ...typography.body, color: markdownPalette.reasoning },
+ statusText: { ...typography.caption, color: palette.dim },
subagent: {
backgroundColor: palette.card,
borderColor: palette.activity,
@@ -1942,9 +1941,9 @@ const styles = StyleSheet.create({
paddingVertical: 8,
},
subagentAction: { justifyContent: "center", minHeight: 44, paddingRight: space.md },
- subagentActionLabel: { color: palette.signal, fontSize: 14, fontWeight: "700" },
+ subagentActionLabel: { ...typography.control, color: palette.signal },
subagentActions: { flexDirection: "row", flexWrap: "wrap" },
- subagentAgent: { color: palette.dim, fontSize: 12 },
+ subagentAgent: { ...typography.caption, color: palette.dim },
subagentHeading: {
alignItems: "center",
flexDirection: "row",
@@ -1955,19 +1954,18 @@ const styles = StyleSheet.create({
subagentHeadingLargeText: { alignItems: "flex-start", flexDirection: "column", gap: space.xs },
subagentLabel: { ...typography.label, color: palette.activity },
subagentResult: {
+ ...typography.body,
borderTopColor: palette.border,
borderTopWidth: StyleSheet.hairlineWidth,
color: palette.ink,
- fontSize: 14,
- lineHeight: 21,
paddingTop: space.sm,
},
subagentState: { ...typography.label, color: palette.dim },
subagentStateError: { color: palette.danger },
subagentStateRunning: { color: palette.activity },
- subagentTitle: { color: palette.ink, fontSize: 16, fontWeight: "700" },
+ subagentTitle: { ...typography.heading, color: palette.ink },
textAction: { alignSelf: "flex-start", minHeight: 44, paddingVertical: 10 },
- textActionLabel: { color: palette.signal, fontSize: 12, fontWeight: "700" },
+ textActionLabel: { ...typography.label, color: palette.signal },
userBubble: {
backgroundColor: palette.prompt,
borderRadius: radius.sm,
@@ -1979,5 +1977,5 @@ const styles = StyleSheet.create({
// Supply intrinsic text width to Yoga; the native selection view supplies height.
textWidthMeasurement: { height: 0, overflow: "hidden", opacity: 0 },
userRow: { alignItems: "flex-end", paddingHorizontal: space.md, paddingVertical: space.md },
- userText: { color: palette.ink, fontSize: 16, lineHeight: 23 },
+ userText: { ...typography.body, color: palette.ink },
});