diff --git a/apps/mobile/src/components/management-layout.test.tsx b/apps/mobile/src/components/management-layout.test.tsx
new file mode 100644
index 0000000..a61433d
--- /dev/null
+++ b/apps/mobile/src/components/management-layout.test.tsx
@@ -0,0 +1,29 @@
+import { expect, test } from "@jest/globals";
+import { render, screen } from "@testing-library/react-native";
+import { Dimensions, StyleSheet, Text, View } from "react-native";
+import { SettingsGroup, SettingsRow } from "./management-layout";
+
+test.each([1, 1.5])(
+ "settings rows adapt to font scale %s without reducing touch targets",
+ (fontScale) => {
+ const originalWindow = Dimensions.get("window");
+ const originalScreen = Dimensions.get("screen");
+ Dimensions.set({ window: { width: 390, height: 844, scale: 3, fontScale } });
+ const view = render(
+
+
+ Detailed transcript
+
+ ,
+ );
+ expect(screen.getByRole("header", { name: "Transcript" })).toBeOnTheScreen();
+ const row = screen.UNSAFE_getByType(SettingsRow).findByType(View);
+ expect(StyleSheet.flatten(row.props.style)).toMatchObject({
+ minHeight: 48,
+ flexDirection: fontScale >= 1.3 ? "column" : "row",
+ alignItems: fontScale >= 1.3 ? "stretch" : "center",
+ });
+ view.unmount();
+ Dimensions.set({ window: originalWindow, screen: originalScreen });
+ },
+);
diff --git a/apps/mobile/src/components/management-layout.tsx b/apps/mobile/src/components/management-layout.tsx
new file mode 100644
index 0000000..033ca63
--- /dev/null
+++ b/apps/mobile/src/components/management-layout.tsx
@@ -0,0 +1,60 @@
+import type { ReactNode } from "react";
+import { StyleSheet, Text, useWindowDimensions, View } from "react-native";
+
+import { palette, radius, space, typography, usesLargeTextLayout } from "../theme";
+
+export function SettingsGroup({ children, title }: { children: ReactNode; title: string }) {
+ return (
+
+
+ {title}
+
+ {children}
+
+ );
+}
+
+export function SettingsRow({ children }: { children: ReactNode }) {
+ const { fontScale } = useWindowDimensions();
+ return (
+
+ {children}
+
+ );
+}
+
+export const managementStyles = StyleSheet.create({
+ content: {
+ alignSelf: "center",
+ width: "100%",
+ maxWidth: 680,
+ padding: space.md,
+ paddingBottom: space.xl,
+ gap: space.md,
+ },
+ description: { ...typography.caption, color: palette.dim },
+ section: { gap: space.xs },
+ sectionTitle: { ...typography.label, color: palette.dim },
+ group: {
+ backgroundColor: palette.card,
+ borderRadius: radius.md,
+ borderColor: palette.border,
+ borderWidth: StyleSheet.hairlineWidth,
+ overflow: "hidden",
+ },
+ row: {
+ alignItems: "center",
+ flexDirection: "row",
+ gap: space.sm,
+ minHeight: 48,
+ padding: space.sm,
+ borderBottomColor: palette.border,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ },
+ rowLargeText: { alignItems: "stretch", flexDirection: "column" },
+});
diff --git a/apps/mobile/src/components/request-card-styles.ts b/apps/mobile/src/components/request-card-styles.ts
new file mode 100644
index 0000000..4603313
--- /dev/null
+++ b/apps/mobile/src/components/request-card-styles.ts
@@ -0,0 +1,29 @@
+import { StyleSheet } from "react-native";
+import { palette, radius, space, typography } from "../theme";
+
+// Question and approval actions share visual sizing without shrinking touch targets.
+export const requestCardStyles = StyleSheet.create({
+ actions: { flexDirection: "row", flexWrap: "wrap", gap: space.xs },
+ button: {
+ minHeight: 44,
+ paddingHorizontal: space.sm,
+ paddingVertical: space.xs,
+ borderRadius: radius.sm,
+ alignItems: "center",
+ justifyContent: "center",
+ borderColor: palette.border,
+ borderWidth: 1,
+ maxWidth: "100%",
+ },
+ buttonLabel: { ...typography.control, color: palette.ink, textAlign: "center" },
+ card: {
+ backgroundColor: palette.card,
+ borderColor: palette.border,
+ borderWidth: 1,
+ borderRadius: radius.md,
+ borderLeftWidth: 3,
+ borderLeftColor: palette.warm,
+ padding: 12,
+ gap: space.sm,
+ },
+});
diff --git a/apps/mobile/src/navigation/root-navigation.tsx b/apps/mobile/src/navigation/root-navigation.tsx
index 10f7923..030c66c 100644
--- a/apps/mobile/src/navigation/root-navigation.tsx
+++ b/apps/mobile/src/navigation/root-navigation.tsx
@@ -153,12 +153,20 @@ export function RootNavigation() {
({
+ presentation: "modal",
+ title: "Settings",
+ headerRight: () => navigation.goBack()} />,
+ })}
/>
({
+ presentation: "modal",
+ title: "Followed projects",
+ headerRight: () => navigation.goBack()} />,
+ })}
/>
void }) {
+ return (
+
+ Done
+
+ );
+}
+
function WorkspaceHeader({
onBack,
title,
diff --git a/apps/mobile/src/screens/app-shell.tsx b/apps/mobile/src/screens/app-shell.tsx
index e333a50..6a986e7 100644
--- a/apps/mobile/src/screens/app-shell.tsx
+++ b/apps/mobile/src/screens/app-shell.tsx
@@ -19,7 +19,7 @@ import {
View,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
-
+import { managementStyles, SettingsGroup, SettingsRow } from "../components/management-layout";
import { ModalSheet } from "../components/modal-sheet";
import { useConnections } from "../connections/connections-context";
import type { RootStackParamList } from "../navigation/root-navigation";
@@ -367,175 +367,187 @@ export function SettingsScreen({ navigation }: ScreenProps<"Settings">) {
return (
-
- SETTINGS
-
- Device and connection controls.
-
+
+ Device and connection controls.
-
-
- Default follow-up delivery
-
- Steer updates the current work. Queue waits until it finishes.
-
-
- {(["steer", "queue"] as const).map((delivery) => (
- void transcript.update({ defaultDelivery: delivery })}
- style={{ minHeight: 48, justifyContent: "center" }}
- >
-
- {transcript.defaultDelivery === delivery ? "✓ " : ""}
- {delivery === "steer" ? "Steer" : "Queue"}
-
-
- ))}
+
+
+
+ Default follow-up delivery
+
+ Steer updates the current work. Queue waits until it finishes.
+
+
+ {(["steer", "queue"] as const).map((delivery) => (
+ void transcript.update({ defaultDelivery: delivery })}
+ style={[
+ styles.deliveryOption,
+ transcript.defaultDelivery === delivery && styles.deliveryOptionSelected,
+ ]}
+ >
+
+ {transcript.defaultDelivery === delivery ? "✓ " : ""}
+ {delivery === "steer" ? "Steer" : "Queue"}
+
+
+ ))}
+
-
-
+
-
-
- Detailed transcript
-
- {transcript.detailed
- ? "Detailed: individual tool executions and system notices."
- : "Compact: group activity and collapse routine details."}
-
-
- void transcript.update({ detailed })}
- />
-
-
-
- Show reasoning
-
- Display model reasoning when provided by the server.
-
-
- void transcript.update({ reasoning })}
- />
-
+
+
+ Detailed transcript
+
+ {transcript.detailed
+ ? "Detailed: individual tool executions and system notices."
+ : "Compact: group activity and collapse routine details."}
+
+
+ void transcript.update({ detailed })}
+ />
+
+
+
+ Show reasoning
+
+ Display model reasoning when provided by the server.
+
+
+ void transcript.update({ reasoning })}
+ />
+
+
{transcript.error ? (
Message preferences could not be loaded or saved. Try changing the setting again.
) : null}
-
-
- App lock
-
- Require device authentication after the app leaves the foreground.
-
-
- void appLock.setEnabled(enabled)}
- value={appLock.enabled}
- />
-
- {appLock.error ? (
-
- {appLock.error}
-
- ) : null}
-
-
-
- Mobile notifications
-
- {notificationLoading
- ? "Reading the shared broker setting."
- : !notificationPairing
- ? "Pair this connection for notifications before enabling delivery."
- : notificationState?.enabled
- ? "Enabled for every phone paired with this notification broker."
- : notificationState
- ? "Paused. New requests will not create mobile notifications."
- : "The shared broker setting is unavailable."}
-
-
- {notificationLoading ? (
-
- ) : (
+
+
+
+ App lock
+
+ Require device authentication after the app leaves the foreground.
+
+
void setNotificationsEnabled(enabled)}
- value={notificationState?.enabled ?? false}
+ accessibilityLabel="Require device authentication"
+ disabled={appLock.busy}
+ onValueChange={(enabled) => void appLock.setEnabled(enabled)}
+ value={appLock.enabled}
/>
- )}
-
+
+ {appLock.error ? (
+
+
+ {appLock.error}
+
+
+ ) : null}
+
+
+
+ Mobile notifications
+
+ {notificationLoading
+ ? "Reading the shared broker setting."
+ : !notificationPairing
+ ? "Pair this connection for notifications before enabling delivery."
+ : notificationState?.enabled
+ ? "Enabled for every phone paired with this notification broker."
+ : notificationState
+ ? "Paused. New requests will not create mobile notifications."
+ : "The shared broker setting is unavailable."}
+
+
+ {notificationLoading ? (
+
+ ) : (
+ void setNotificationsEnabled(enabled)}
+ value={notificationState?.enabled ?? false}
+ />
+ )}
+
+
{notificationError ? (
The notification broker setting could not be read or updated.
) : null}
-
- SESSION INBOX
- Followed projects
-
- Choose which server projects are merged into Sessions and attention reconciliation.
-
- navigation.navigate("FollowedProjects")}
- />
-
+
+
+ Followed projects
+
+ Choose which server projects are merged into Sessions and attention reconciliation.
+
+ navigation.navigate("FollowedProjects")}
+ />
+
-
- SELECTED CONNECTION
- {selected?.name ?? "No connection selected"}
-
- {selected?.baseUrl ?? "Choose a saved server profile."}
-
- navigation.navigate("Connections")}
- />
-
+
+ Selected connection
+ {selected?.name ?? "No connection selected"}
+
+ {selected?.baseUrl ?? "Choose a saved server profile."}
+
+ navigation.navigate("Connections")}
+ />
+
+
-
- SUPPORT
- Redacted transport and transcript report
-
- Includes status transitions, event types, and numeric transcript performance counters.
- It excludes credentials, server addresses, paths, titles, prompts, and content.
-
- void shareDiagnostics()}
- secondary
- />
-
+
+
+ Redacted transport and transcript report
+
+ Includes status transitions, event types, and numeric transcript performance counters.
+ It excludes credentials, server addresses, paths, titles, prompts, and content.
+
+ void shareDiagnostics()}
+ secondary
+ />
+
+
);
@@ -1038,6 +1050,21 @@ export function ActionButton({
}
const styles = StyleSheet.create({
+ deliveryOptions: { flexDirection: "row", flexWrap: "wrap", gap: space.xs, marginTop: space.sm },
+ deliveryOption: {
+ minHeight: 44,
+ minWidth: 88,
+ justifyContent: "center",
+ alignItems: "center",
+ paddingHorizontal: space.sm,
+ borderRadius: radius.sm,
+ },
+ deliveryOptionSelected: { backgroundColor: palette.background },
+ managementAction: {
+ padding: space.sm,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: palette.border,
+ },
locationControls: { flex: 1, minWidth: 0, flexDirection: "row", alignItems: "center" },
locationControlsLargeText: { flex: 0, width: "100%" },
sessionTabs: {
@@ -1255,18 +1282,7 @@ const styles = StyleSheet.create({
width: "100%",
},
stateActions: { flexDirection: "row", flexWrap: "wrap", gap: space.sm },
- settingCard: {
- alignItems: "center",
- backgroundColor: palette.card,
- borderColor: palette.border,
- borderRadius: radius.lg,
- borderWidth: 1,
- flexDirection: "row",
- gap: space.md,
- marginTop: space.lg,
- padding: space.md,
- },
- settingText: { flex: 1 },
+ settingText: { flexGrow: 1, flexShrink: 1 },
shell: { flex: 1 },
stateCard: {
alignItems: "center",
diff --git a/apps/mobile/src/screens/connection-screen.tsx b/apps/mobile/src/screens/connection-screen.tsx
index 440a650..c533226 100644
--- a/apps/mobile/src/screens/connection-screen.tsx
+++ b/apps/mobile/src/screens/connection-screen.tsx
@@ -481,20 +481,16 @@ export function ConnectionScreen({ onDone, onPair }: { onDone?: () => void; onPa
>
-
-
- OPENCODE MOBILE
-
+
+ Connections
+
{onDone ? (
- DONE
+ Done
) : null}
-
- Connections
-
Switch servers or add a direct OpenCode V2 connection.
{onPair ? (
void; onPa
pressed && styles.secondaryButtonPressed,
]}
>
- PAIR SERVER
+ Pair server
) : null}
- SAVED SERVERS
+ Saved servers
void; onPa
pressed && styles.secondaryButtonPressed,
]}
>
- ADD
+ Add
{!connections.ready ? : null}
@@ -549,7 +545,7 @@ export function ConnectionScreen({ onDone, onPair }: { onDone?: () => void; onPa
{profile.name}
{connections.selectedProfileId === profile.id ? (
- SELECTED
+ ✓ Selected
) : null}
@@ -569,7 +565,7 @@ export function ConnectionScreen({ onDone, onPair }: { onDone?: () => void; onPa
pressed && styles.secondaryButtonPressed,
]}
>
- EDIT
+ Edit
void; onPa
]}
>
- {pendingRemovalId === profile.id ? "CONFIRM" : "REMOVE"}
+ {pendingRemovalId === profile.id ? "Confirm" : "Remove"}
@@ -1173,8 +1169,8 @@ const styles = StyleSheet.create({
content: {
alignSelf: "center",
paddingBottom: space.xl,
- paddingHorizontal: space.lg,
- paddingTop: space.lg,
+ paddingHorizontal: space.md,
+ paddingTop: space.sm,
width: "100%",
maxWidth: 680,
},
@@ -1248,9 +1244,6 @@ const styles = StyleSheet.create({
minWidth: 0,
},
closeButton: {
- borderColor: palette.border,
- borderRadius: radius.sm,
- borderWidth: 1,
marginLeft: space.sm,
minHeight: 44,
paddingHorizontal: space.md,
@@ -1272,9 +1265,9 @@ const styles = StyleSheet.create({
paddingVertical: 12,
},
intro: {
- ...typography.body,
+ ...typography.caption,
color: palette.dim,
- marginTop: space.md,
+ marginTop: space.xs,
maxWidth: 560,
},
label: {
@@ -1346,13 +1339,19 @@ const styles = StyleSheet.create({
borderColor: palette.border,
borderRadius: radius.md,
borderWidth: 1,
- flexDirection: "row",
+ flexDirection: "column",
overflow: "hidden",
},
profileCardLargeText: {
flexDirection: "column",
},
- profileActions: { borderLeftColor: palette.border, borderLeftWidth: 1 },
+ profileActions: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ justifyContent: "flex-end",
+ borderTopColor: palette.border,
+ borderTopWidth: StyleSheet.hairlineWidth,
+ },
profileActionsLargeText: {
borderLeftWidth: 0,
borderTopColor: palette.border,
@@ -1367,9 +1366,9 @@ const styles = StyleSheet.create({
justifyContent: "space-between",
},
profileMain: {
- flex: 1,
+ minHeight: 44,
gap: 4,
- padding: space.md,
+ padding: space.sm,
},
profileName: {
...typography.heading,
@@ -1382,7 +1381,8 @@ const styles = StyleSheet.create({
gap: space.sm,
},
profileOrigin: {
- ...typography.code,
+ fontFamily: typography.code.fontFamily,
+ ...typography.caption,
color: palette.dim,
},
profileSection: {
@@ -1494,8 +1494,8 @@ const styles = StyleSheet.create({
textAlign: "center",
},
sectionTitle: {
- ...typography.heading,
- color: palette.ink,
+ ...typography.label,
+ color: palette.dim,
},
selectedLabel: {
...typography.label,
@@ -1503,9 +1503,6 @@ const styles = StyleSheet.create({
},
smallButton: {
alignItems: "center",
- borderColor: palette.border,
- borderRadius: radius.sm,
- borderWidth: 1,
justifyContent: "center",
minHeight: 44,
paddingHorizontal: space.sm,
@@ -1568,9 +1565,9 @@ const styles = StyleSheet.create({
padding: space.lg,
},
title: {
- ...typography.title,
+ ...typography.sheetTitle,
color: palette.ink,
- marginTop: space.lg,
+ flexShrink: 1,
maxWidth: 600,
},
httpLabel: {
diff --git a/apps/mobile/src/screens/followed-projects-screen.tsx b/apps/mobile/src/screens/followed-projects-screen.tsx
index df21f0c..8315bd2 100644
--- a/apps/mobile/src/screens/followed-projects-screen.tsx
+++ b/apps/mobile/src/screens/followed-projects-screen.tsx
@@ -1,10 +1,10 @@
import type { NativeStackScreenProps } from "@react-navigation/native-stack";
import { useState } from "react";
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View } from "react-native";
-
+import { managementStyles } from "../components/management-layout";
import type { RootStackParamList } from "../navigation/root-navigation";
import { useWorkspaceSelection } from "../state/workspace-selection-context";
-import { control, palette, radius, space, typeRamp, typography } from "../theme";
+import { palette, radius, space, typeRamp, typography } from "../theme";
import { ShellFrame } from "./app-shell";
import { sanitizeTranscriptText } from "./session-transcript-model";
@@ -68,15 +68,10 @@ export function FollowedProjectsScreen({ navigation }: Props) {
section === "Workspace" ? navigation.popTo("Workspace") : navigation.navigate(section)
}
>
-
-
- FOLLOWED PROJECTS
-
-
- Choose the projects in your session inbox.
-
+
- Following is stored on this device. It does not create, modify, or remove server projects.
+ Choose the projects in your session inbox. Following is saved on this device only and does
+ not change server projects.
{selection.preferencesLoading || selection.projectsLoading ? (
@@ -131,7 +126,7 @@ export function FollowedProjectsScreen({ navigation }: Props) {
{path}
- {selected ? "FOLLOWING" : "NOT FOLLOWED"}
+ {selected ? "✓ Following" : "Not followed"}
{selected ? (
@@ -221,19 +216,22 @@ function projectLabel(project: { canonical: string; id: string; name?: string })
}
const styles = StyleSheet.create({
- content: { gap: space.md, padding: space.lg, paddingBottom: space.xl },
- copy: { ...typography.body, color: palette.dim },
+ copy: { ...typography.caption, color: palette.dim },
disabled: { opacity: 0.5 },
error: { ...typography.body, color: palette.danger },
- eyebrow: { ...typography.label, color: palette.dim },
followState: { ...typography.label, color: palette.dim },
followStateSelected: { color: palette.signal },
list: { gap: space.sm },
- orderActions: { flexDirection: "row", flexWrap: "wrap", gap: space.sm, padding: space.sm },
+ orderActions: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ gap: space.xs,
+ paddingHorizontal: space.sm,
+ paddingBottom: space.xs,
+ },
orderButton: {
- ...control,
- borderColor: palette.border,
- borderWidth: 1,
+ minHeight: 44,
+ paddingHorizontal: space.sm,
justifyContent: "center",
},
orderLabel: { ...typography.control, color: palette.ink },
@@ -244,12 +242,11 @@ const styles = StyleSheet.create({
borderRadius: radius.md,
borderWidth: 1,
},
- projectMain: { gap: space.xs, minHeight: 72, padding: space.md },
+ projectMain: { gap: 4, minHeight: 72, padding: space.sm },
projectPath: { ...typography.caption, color: palette.dim },
- projectSelected: { borderColor: palette.signal },
+ projectSelected: { borderColor: palette.border },
projectTitle: { ...typography.heading, color: palette.ink },
state: { alignItems: "center", gap: space.sm, paddingVertical: space.xl },
- title: { ...typography.title, color: palette.ink },
unavailableProject: {
backgroundColor: palette.card,
borderColor: palette.warm,
diff --git a/apps/mobile/src/screens/form-request-card.tsx b/apps/mobile/src/screens/form-request-card.tsx
index 59dc798..877e8e0 100644
--- a/apps/mobile/src/screens/form-request-card.tsx
+++ b/apps/mobile/src/screens/form-request-card.tsx
@@ -14,7 +14,8 @@ import {
import { applicationName } from "../application-name";
import { ModalSheet } from "../components/modal-sheet";
-import { control, palette, radius, space, typeRamp, typography } from "../theme";
+import { requestCardStyles } from "../components/request-card-styles";
+import { palette, radius, space, typeRamp, typography } from "../theme";
import {
createFormDraft,
type FormDraft,
@@ -541,34 +542,19 @@ const styles = StyleSheet.create({
borderTopColor: palette.border,
},
choiceHeading: { flexDirection: "row", gap: space.sm, alignItems: "center" },
- actions: { flexDirection: "row", flexWrap: "wrap", gap: space.sm },
+ actions: requestCardStyles.actions,
booleanOptions: {
flexDirection: "row",
flexWrap: "wrap",
gap: space.sm,
},
- button: {
- ...control,
- alignItems: "center",
- borderColor: palette.border,
- borderWidth: 1,
- justifyContent: "center",
- },
+ button: requestCardStyles.button,
buttonPrimary: { backgroundColor: palette.signal },
buttonPrimaryLabel: { color: palette.background },
buttonDanger: { borderColor: palette.border },
buttonDangerLabel: { color: palette.danger },
- buttonLabel: { ...typography.control, color: palette.ink, textAlign: "center" },
- card: {
- backgroundColor: palette.card,
- borderColor: palette.border,
- borderRadius: radius.lg,
- borderWidth: 1,
- borderLeftWidth: 3,
- borderLeftColor: palette.warm,
- gap: space.md,
- padding: space.md,
- },
+ buttonLabel: requestCardStyles.buttonLabel,
+ card: requestCardStyles.card,
choice: {
borderColor: palette.border,
borderRadius: radius.sm,
diff --git a/apps/mobile/src/screens/permission-request-card.test.tsx b/apps/mobile/src/screens/permission-request-card.test.tsx
index 1819273..091a922 100644
--- a/apps/mobile/src/screens/permission-request-card.test.tsx
+++ b/apps/mobile/src/screens/permission-request-card.test.tsx
@@ -1,6 +1,6 @@
import { expect, jest, test } from "@jest/globals";
import { fireEvent, render, screen } from "@testing-library/react-native";
-import { palette, typography } from "../theme";
+import { palette, radius, space, typography } from "../theme";
import { PermissionRequestCard } from "./permission-request-card";
const command = `python3 - <<'PY'\n${"print('review me')\n".repeat(100)}PY`;
@@ -87,7 +87,12 @@ test("compact approval actions retain all three reply values", () => {
["Reject", "reject"],
] as const) {
const button = screen.getByRole("button", { name });
- expect(button).toHaveStyle({ minHeight: 44 });
+ expect(button).toHaveStyle({
+ minHeight: 44,
+ borderRadius: radius.sm,
+ paddingHorizontal: space.sm,
+ });
+ expect(button).not.toHaveStyle({ flexGrow: 1 });
fireEvent.press(button);
expect(reply).toHaveBeenLastCalledWith(request.id, request.sessionID, value);
}
diff --git a/apps/mobile/src/screens/permission-request-card.tsx b/apps/mobile/src/screens/permission-request-card.tsx
index 7e401f5..3a1112b 100644
--- a/apps/mobile/src/screens/permission-request-card.tsx
+++ b/apps/mobile/src/screens/permission-request-card.tsx
@@ -3,15 +3,8 @@ import type { PermissionReply, PermissionRequest } from "@opencode2-mobile/openc
import { useState } from "react";
import { Pressable, StyleSheet, Text, useWindowDimensions, View } from "react-native";
import { ModalSheet } from "../components/modal-sheet";
-import {
- control,
- markdownPalette,
- palette,
- radius,
- space,
- typography,
- usesLargeTextLayout,
-} from "../theme";
+import { requestCardStyles } from "../components/request-card-styles";
+import { markdownPalette, palette, radius, space, typography, usesLargeTextLayout } from "../theme";
import { permissionActionExplanation } from "./permission-presentation";
import { sanitizeTranscriptText } from "./session-transcript-model";
@@ -161,14 +154,7 @@ const styles = StyleSheet.create({
headingRow: { flexDirection: "row", alignItems: "center", gap: space.xs },
headingTitle: { flex: 1 },
card: {
- backgroundColor: palette.card,
- borderColor: palette.border,
- borderWidth: 1,
- borderRadius: radius.md,
- borderLeftWidth: 3,
- borderLeftColor: palette.warm,
- padding: 12,
- gap: 8,
+ ...requestCardStyles.card,
},
title: { ...typography.heading, color: palette.ink },
action: { ...typography.body, color: palette.ink },
@@ -186,28 +172,18 @@ const styles = StyleSheet.create({
caption: { ...typography.caption, color: palette.dim },
copy: { ...typography.body, color: palette.dim, marginVertical: space.sm },
actions: {
- flexDirection: "row",
- flexWrap: "wrap",
- gap: 8,
+ ...requestCardStyles.actions,
paddingTop: space.sm,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: palette.border,
},
- actionsLarge: { flexDirection: "column" },
+ actionsLarge: { flexDirection: "column", alignItems: "flex-start" },
button: {
- ...control,
- minHeight: 44,
- paddingHorizontal: space.sm,
- paddingVertical: space.xs,
- flexGrow: 1,
- justifyContent: "center",
- alignItems: "center",
- borderColor: palette.border,
- borderWidth: 1,
+ ...requestCardStyles.button,
},
primary: { backgroundColor: palette.signal },
primaryLabel: { color: palette.background },
- buttonLabel: { ...typography.control, color: palette.ink, textAlign: "center" },
+ buttonLabel: requestCardStyles.buttonLabel,
reject: { color: palette.danger },
error: { ...typography.body, color: palette.danger },
disabled: { opacity: 0.5 },