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
2 changes: 2 additions & 0 deletions app/(tabs)/apps.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { View, Text, ScrollView, RefreshControl } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { webContentMaxWidth } from "~/lib/responsive";
import { useRouter } from "expo-router";
import { useTranslation } from "react-i18next";
import { useCallback, useState } from "react";
Expand Down Expand Up @@ -34,6 +35,7 @@ export default function AppsScreen() {
<ScrollView
className="flex-1"
contentContainerClassName="px-5 pb-8 pt-4"
contentContainerStyle={webContentMaxWidth}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl refreshing={isRefreshing} onRefresh={onRefresh} tintColor="#1e40af" />
Expand Down
2 changes: 2 additions & 0 deletions app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { View, Text, ScrollView, RefreshControl } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { webContentMaxWidth } from "~/lib/responsive";
import {
LayoutDashboard,
ChevronRight,
Expand Down Expand Up @@ -115,6 +116,7 @@ export default function HomeScreen() {
<ScrollView
className="flex-1"
contentContainerClassName="px-5 pb-8 pt-4"
contentContainerStyle={webContentMaxWidth}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl refreshing={isRefreshing} onRefresh={onRefresh} tintColor="#1e40af" />
Expand Down
7 changes: 6 additions & 1 deletion app/(tabs)/more.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { View, Text, ScrollView, TouchableOpacity } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { webContentMaxWidth } from "~/lib/responsive";
import {
UserCircle,
Bell,
Expand Down Expand Up @@ -81,7 +82,11 @@ export default function MoreScreen() {

return (
<SafeAreaView className="flex-1 bg-background" edges={["top", "left", "right"]}>
<ScrollView className="flex-1" showsVerticalScrollIndicator={false}>
<ScrollView
className="flex-1"
contentContainerStyle={webContentMaxWidth}
showsVerticalScrollIndicator={false}
>
{/* Profile Header */}
<TouchableOpacity
className="flex-row items-center px-5 py-5 border-b border-border/30"
Expand Down
2 changes: 2 additions & 0 deletions app/(tabs)/notifications.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { View, Text, ScrollView, Pressable } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { webContentMaxWidth } from "~/lib/responsive";
import { Bell, CheckCheck, Circle, WifiOff } from "lucide-react-native";
import { useRouter } from "expo-router";
import { useTranslation } from "react-i18next";
Expand Down Expand Up @@ -146,6 +147,7 @@ export default function NotificationsScreen() {
<ScrollView
className="flex-1"
contentContainerClassName="px-1 pb-8 pt-2"
contentContainerStyle={webContentMaxWidth}
>
{notifications.map((n) => (
<NotificationRow key={n.id} notification={n} onPress={handlePress} />
Expand Down
2 changes: 2 additions & 0 deletions app/(tabs)/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { View, Text, ScrollView } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { webContentMaxWidth } from "~/lib/responsive";
import { UserCircle } from "lucide-react-native";
import { useRouter } from "expo-router";
import { Button } from "~/components/ui/Button";
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function ProfileScreen() {
<ScrollView
className="flex-1"
contentContainerClassName="px-5 pb-8 pt-4"
contentContainerStyle={webContentMaxWidth}
>
<View className="items-center pt-10">
<View className="rounded-full bg-muted p-5">
Expand Down
2 changes: 2 additions & 0 deletions app/(tabs)/search.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { View, Text, TouchableOpacity, ScrollView, ActivityIndicator } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { webContentMaxWidth } from "~/lib/responsive";
import { Search as SearchIcon, X, ChevronRight, FileText, SearchX } from "lucide-react-native";
import { useRouter } from "expo-router";
import { useTranslation } from "react-i18next";
Expand Down Expand Up @@ -68,6 +69,7 @@ export default function SearchScreen() {
<ScrollView
className="flex-1"
contentContainerClassName="px-5 pb-8 pt-4"
contentContainerStyle={webContentMaxWidth}
keyboardShouldPersistTaps="handled"
>
<Text className="mb-3 text-xs font-medium text-muted-foreground">
Expand Down
9 changes: 7 additions & 2 deletions app/ai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { useTranslation } from "react-i18next";
import { webContentMaxWidth } from "~/lib/responsive";
import * as Clipboard from "expo-clipboard";
import {
Send,
Expand Down Expand Up @@ -305,6 +306,7 @@ export default function AIAssistantScreen() {
ref={scrollRef}
className="flex-1"
contentContainerClassName="px-4 py-4"
contentContainerStyle={webContentMaxWidth}
keyboardShouldPersistTaps="handled"
>
{messages.length === 0 ? (
Expand Down Expand Up @@ -356,8 +358,11 @@ export default function AIAssistantScreen() {
)}
</ScrollView>

{/* Input bar */}
<View className="flex-row items-end gap-2 border-t border-border bg-card px-3 py-2.5">
{/* Input bar — capped + centered on web to align with the message column */}
<View
className="w-full flex-row items-end gap-2 border-t border-border bg-card px-3 py-2.5"
style={webContentMaxWidth}
>
<TextInput
className="max-h-28 flex-1 rounded-2xl border border-input bg-background px-4 py-2.5 text-base text-foreground"
value={draft}
Expand Down
2 changes: 2 additions & 0 deletions components/renderers/DetailViewRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo, useState } from "react";
import { View, Text, ScrollView, Pressable, ActivityIndicator } from "react-native";
import { webContentMaxWidth } from "~/lib/responsive";
import {
Edit,
Trash2,
Expand Down Expand Up @@ -567,6 +568,7 @@ export function DetailViewRenderer({
<ScrollView
className="flex-1"
contentContainerClassName="px-4 pb-8 pt-4"
contentContainerStyle={webContentMaxWidth}
>
{/* Field sections */}
{sections.map((section, idx) => {
Expand Down
2 changes: 2 additions & 0 deletions components/renderers/FormViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "react-native";
import { ChevronDown, ChevronUp } from "lucide-react-native";
import { Button } from "~/components/ui/Button";
import { webContentMaxWidth } from "~/lib/responsive";
import {
isFieldVisible,
isFieldReadonlyByCondition,
Expand Down Expand Up @@ -310,6 +311,7 @@ export function FormViewRenderer({
<ScrollView
className="flex-1"
contentContainerClassName="px-4 pb-8 pt-4"
contentContainerStyle={webContentMaxWidth}
keyboardShouldPersistTaps="handled"
>
{sections.map((section, idx) => {
Expand Down
6 changes: 5 additions & 1 deletion components/renderers/ListViewRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
RefreshControl,
} from "react-native";
import { FlashList } from "@shopify/flash-list";
import { webContentMaxWidth } from "~/lib/responsive";
import { ChevronDown, ChevronUp, Check, Search as SearchIcon, AlertCircle } from "lucide-react-native";
import { cn } from "~/lib/utils";
import { EmptyState } from "~/components/common/EmptyState";
Expand Down Expand Up @@ -392,7 +393,10 @@ export function ListViewRenderer({

const rowContent = (
<Pressable
style={customBg ? { backgroundColor: customBg } : undefined}
// Cap + center each row on wide web so the list reads as a column,
// not edge-to-edge (no-op on native). Per-row keeps FlashList's
// virtualization intact — its contentContainerStyle is padding-only.
style={[webContentMaxWidth, customBg ? { backgroundColor: customBg } : null]}
className={cn(
"mb-2 rounded-xl px-4 active:bg-muted/50",
densityClass,
Expand Down
18 changes: 18 additions & 0 deletions lib/responsive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Platform, type ViewStyle } from "react-native";

/**
* Reading-column width cap for wide web viewports.
*
* On the web the app runs in a full-width browser window, so lists, cards, and
* chat bubbles stretch edge-to-edge and become hard to scan. Spreading this
* into a scroll view's `contentContainerStyle` (or a wrapping View's `style`)
* caps the content column and centers it.
*
* It is a deliberate **no-op on native** (and on narrow web, where `maxWidth`
* simply never binds) — the device viewport is already width-constrained, so
* this never changes the phone layout.
*/
export const webContentMaxWidth: ViewStyle | undefined =
Platform.OS === "web"
? { width: "100%", maxWidth: 820, alignSelf: "center" }
: undefined;
Loading