Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions apps/mobile/src/screens/form-request-card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -29,6 +30,17 @@ const form: FormInfo = {
title: "Deploy",
};

test("question controls use shared typography and neutral attention colors", () => {
render(<FormRequestCard form={form} onCancel={jest.fn()} onSubmit={jest.fn()} />);

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(<FormRequestCard form={form} onCancel={jest.fn()} onSubmit={onSubmit} />);
Expand Down
20 changes: 15 additions & 5 deletions apps/mobile/src/screens/form-request-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,17 @@ function ChoiceButton({
pressed && styles.pressed,
]}
>
<Text style={[styles.choiceLabel, selected && styles.choiceLabelSelected]}>{label}</Text>
{description ? <Text style={styles.description}>{description}</Text> : null}
<Text
dynamicTypeRamp={typeRamp.control}
style={[styles.choiceLabel, selected && styles.choiceLabelSelected]}
>
{label}
</Text>
{description ? (
<Text dynamicTypeRamp={typeRamp.caption} style={styles.description}>
{description}
</Text>
) : null}
</Pressable>
);
}
Expand Down Expand Up @@ -368,6 +377,7 @@ function FormButton({
]}
>
<Text
dynamicTypeRamp={typeRamp.control}
style={[
styles.buttonLabel,
primary && styles.buttonPrimaryLabel,
Expand Down Expand Up @@ -450,23 +460,23 @@ const styles = StyleSheet.create({
minHeight: 44,
padding: space.sm,
},
choiceLabel: { color: palette.ink, fontSize: 14, fontWeight: "700" },
choiceLabel: { ...typography.control, color: palette.ink },
choiceLabelSelected: { color: palette.signal },
choiceSelected: { backgroundColor: palette.signalDark, borderColor: palette.signal },
constraint: { ...typography.caption, color: palette.dim },
description: { ...typography.caption, color: palette.dim },
disabled: { opacity: 0.5 },
error: { ...typography.body, color: palette.danger },
eyebrow: { ...typography.heading, color: palette.warm },
eyebrow: { ...typography.label, color: palette.dim },
field: { gap: space.xs },
fieldHeading: { gap: 2 },
input: {
...typography.body,
backgroundColor: palette.background,
borderColor: palette.border,
borderRadius: radius.sm,
borderWidth: 1,
color: palette.ink,
fontSize: 15,
minHeight: 44,
paddingHorizontal: space.sm,
paddingVertical: space.sm,
Expand Down
13 changes: 13 additions & 0 deletions apps/mobile/src/screens/permission-request-card.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, jest, test } from "@jest/globals";
import { fireEvent, render, screen } from "@testing-library/react-native";
import { palette, typography } from "../theme";
import { PermissionRequestCard } from "./permission-request-card";

const command = `python3 - <<'PY'\n${"print('review me')\n".repeat(100)}PY`;
Expand All @@ -11,6 +12,18 @@ const request = {
save: ["python3 *"],
};

test("permission messages use neutral headings and shared error typography", () => {
render(<PermissionRequestCard request={request} replying={false} error onReply={jest.fn()} />);
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(
Expand Down
5 changes: 3 additions & 2 deletions apps/mobile/src/screens/permission-request-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export function PermissionRequestCard({
</Text>
) : null}
{error ? (
<Text accessibilityRole="alert" style={styles.reject}>
<Text accessibilityRole="alert" style={styles.error}>
The server did not accept that reply. Review the refreshed request and try again.
</Text>
) : null}
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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" },
});
23 changes: 21 additions & 2 deletions apps/mobile/src/screens/session-transcript.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
<SessionTranscriptRow
message={{ ...message, content: [{ type: "text", text: "Themed response" }] }}
/>,
);
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(),
Expand Down Expand Up @@ -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", () => {
Expand Down
44 changes: 21 additions & 23 deletions apps/mobile/src/screens/session-transcript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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",
Expand All @@ -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%" },
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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",
Expand All @@ -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,
Expand All @@ -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 },
});
Loading