Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f5fce74
fix(pull-requests): route self-hosted GitLab remotes (#6061)
GuilhermeVieiraDev Aug 11, 2026
752acbf
feat: add ability to create a new thread in the current project with …
UtkarshUsername Aug 11, 2026
65b005f
feat(web): add Copy Thread ID to the sidebar and chat header thread c…
UtkarshUsername Aug 11, 2026
6676f9c
fix(mobile): stabilize thread composer and interactions (#5986)
juliusmarminge Aug 11, 2026
c842c6f
Add hourly past-24-hour usage view (#6170)
juliusmarminge Aug 11, 2026
3da7f9c
fix(mobile): guard App Store release versions (#6177)
juliusmarminge Aug 11, 2026
ac4780f
fix(web): restore typography font sizes to defaults (#6172)
StiensWout Aug 11, 2026
b30a9bc
feat(web): make environment artwork theme aware (#6183)
juliusmarminge Aug 11, 2026
6befe42
fix(shared): normalize a bare Windows drive root the same as C:\ / C:…
arhxam Aug 11, 2026
220e573
fix(shared): detect Azure DevOps SSH remotes (ssh.dev.azure.com) (#6187)
arhxam Aug 11, 2026
44621c3
feat(web): add back buttons for the pull requests and usage pages in …
UtkarshUsername Aug 11, 2026
1e355a2
fix(web): render dropdowns above toasts (#6165)
Brechard Aug 11, 2026
57b1052
fix(web): thread error banner dismiss survives reconnect and rerender…
myacoub91 Aug 11, 2026
3517201
fix(web): use a clearer pull action icon (#6194)
extoci Aug 11, 2026
083fa4a
feat(web): use OKLCH for theme palettes (#6036)
StiensWout Aug 11, 2026
3298a1a
merge: sync upstream through 083fa4ab2
omegent-app[bot] Aug 11, 2026
17adee9
fix(mobile): scope the feed mount latch to the environment
omegent-app[bot] Aug 11, 2026
e5b838f
fix(mobile): one scroll-to-end control, not two
omegent-app[bot] Aug 11, 2026
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
14 changes: 14 additions & 0 deletions .github/workflows/mobile-eas-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ on:
- ios
- android
- all
version:
description: "Optional build version override (blank uses app.config.ts; an override is committed before building)"
required: false
type: string
message:
description: "OTA update message (mode=update / auto)"
required: false
Expand Down Expand Up @@ -79,12 +83,22 @@ jobs:
echo "EXPO_TOKEN is not available; skipping EAS production job."
fi

- id: version_app_token
name: Mint release app token for version override
if: steps.expo-token.outputs.present == 'true' && github.event_name == 'workflow_dispatch' && inputs.mode == 'build' && inputs.version != ''
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}

- name: Checkout
if: steps.expo-token.outputs.present == 'true'
uses: actions/checkout@v6
with:
ref: ${{ inputs.release_branch }}
fetch-depth: 0
token: ${{ steps.version_app_token.outputs.token || github.token }}
# No sparse-checkout here: it makes actions/checkout fetch with
# --filter=blob:none, and eas-cli archives the project via
# `git clone --depth 1 file://<workspace>`, which fails (exit 128)
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ const config: ExpoConfig = {
slug: "t3-code",
platforms: ["ios", "android"],
scheme: variant.scheme,
version: "1.0.2",
version: "1.0.3",
// Default: fingerprint policy so OTAs only reach binaries with matching native
// inputs (deps, config plugins, patches). Override with MOBILE_RUNTIME_VERSION_OVERRIDE
// when CI must ship pure-JS fixes to an already-installed binary (e.g. Free-plan
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@expo-google-fonts/dm-sans": "^0.4.2",
"@expo/metro-runtime": "~56.0.15",
"@expo/ui": "~56.0.18",
"@legendapp/list": "3.3.3",
"@legendapp/list": "catalog:",
"@noble/curves": "catalog:",
"@noble/hashes": "catalog:",
"@pierre/diffs": "catalog:",
Expand Down
33 changes: 20 additions & 13 deletions apps/mobile/src/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { useResolveClassNames } from "uniwind";
import { AppText as Text } from "./components/AppText";
import { ArchivedThreadsRouteScreen } from "./features/archive/ArchivedThreadsRouteScreen";
import { useAgentNotificationNavigation } from "./features/agent-awareness/notificationNavigation";
import { ClerkSettingsSheetDetentProvider } from "./features/cloud/ClerkSettingsSheetDetent";
import { ConnectOnboardingRouteScreen } from "./features/cloud/ConnectOnboardingRouteScreen";
import { useConnectOnboardingNavigation } from "./features/cloud/connectOnboardingNavigation";
import { ThreadFilesTreeScreen, ThreadFileScreen } from "./features/files/ThreadFilesRouteScreen";
Expand Down Expand Up @@ -134,7 +133,7 @@ const LEGAL_DOCUMENT_HEADER_OPTIONS: AppScreenOptions = {
presentation: "fullScreenModal",
};

const SettingsSheetStack = createNativeStackNavigator({
const SettingsContentStack = createNativeStackNavigator({
initialRouteName: "Settings",
screenOptions: {
...GLASS_HEADER_OPTIONS,
Expand Down Expand Up @@ -198,20 +197,30 @@ const SettingsSheetStack = createNativeStackNavigator({
title: "Usage",
},
}),
},
});

// The outer stack never owns visible chrome. Settings routes render inside a
// nested stack whose native header remains mounted, while Clerk owns auth chrome.
// Keeping bar visibility invariant avoids iOS 26's headerless-to-headered jump.
const SettingsSheetStack = createNativeStackNavigator({
initialRouteName: "SettingsContent",
screenOptions: {
headerShown: false,
},
screens: {
SettingsContent: createNativeStackScreen({
screen: SettingsContentStack,
linking: "",
}),
SettingsAuth: createNativeStackScreen({
screen: SettingsAuthRouteScreen,
linking: "auth",
options: {
title: "Sign in",
},
}),
SettingsWaitlist: createNativeStackScreen({
// Keep the old deep link working after the Connect GA launch.
screen: SettingsAuthRouteScreen,
linking: "waitlist",
options: {
title: "Sign in",
},
}),
},
});
Expand Down Expand Up @@ -347,11 +356,9 @@ function RootStackLayout(props: {
<HardwareKeyboardCommandProvider pathname={pathname}>
<ThreadOutboxDrainWorker />
<ShowcaseCaptureCoordinator pathname={pathname} />
<ClerkSettingsSheetDetentProvider initiallyExpanded={false}>
<AdaptiveWorkspaceLayout pathname={workspacePathname}>
{props.children}
</AdaptiveWorkspaceLayout>
</ClerkSettingsSheetDetentProvider>
<AdaptiveWorkspaceLayout pathname={workspacePathname}>
{props.children}
</AdaptiveWorkspaceLayout>
</HardwareKeyboardCommandProvider>
);
}
Expand Down
27 changes: 26 additions & 1 deletion apps/mobile/src/components/ControlPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type ComponentProps,
type ReactElement,
type ReactNode,
useRef,
} from "react";
import { Platform, Pressable, useColorScheme, View } from "react-native";
import { useThemeColor } from "../lib/useThemeColor";
Expand All @@ -21,10 +22,31 @@ export function ControlPill(props: {
readonly label?: string;
readonly accessibilityLabel?: string;
readonly onPress?: () => void;
readonly activateOnPressIn?: boolean;
readonly variant?: "circle" | "pill" | "primary" | "danger";
readonly disabled?: boolean;
readonly className?: string;
}) {
const variant = props.variant ?? "circle";
const activatedOnPressInRef = useRef(false);

const handlePressIn = () => {
activatedOnPressInRef.current = true;
props.onPress?.();
};
const handlePressOut = () => {
// Pressability invokes onPressOut immediately before onPress on release.
// Defer the reset so onPress can identify the same physical gesture.
setTimeout(() => {
activatedOnPressInRef.current = false;
}, 0);
};
const handlePress = () => {
if (activatedOnPressInRef.current) {
return;
}
props.onPress?.();
};

const iconColor = useThemeColor("--color-icon");
const iconSubtle = useThemeColor("--color-icon-subtle");
Expand Down Expand Up @@ -54,6 +76,7 @@ export function ControlPill(props: {
: variant === "danger"
? "bg-danger"
: "bg-subtle",
props.className,
);
const labelClassName = cn(
"text-center text-xs font-t3-bold",
Expand All @@ -68,7 +91,9 @@ export function ControlPill(props: {
<Pressable
accessibilityLabel={props.accessibilityLabel ?? props.label}
accessibilityRole="button"
onPress={props.onPress}
onPress={props.activateOnPressIn ? handlePress : props.onPress}
onPressIn={props.activateOnPressIn ? handlePressIn : undefined}
onPressOut={props.activateOnPressIn ? handlePressOut : undefined}
disabled={props.disabled}
className={containerClassName}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useFocusEffect } from "@react-navigation/native";
import { useCallback, useMemo, useState } from "react";

import { useSavedRemoteConnections } from "../../state/use-remote-environment-registry";
import { useClerkSettingsSheetDetent } from "../cloud/ClerkSettingsSheetDetent";
import { useArchivedThreadListActions } from "../home/useThreadListActions";
import {
ArchivedThreadsScreen,
Expand All @@ -18,7 +17,6 @@ import {
} from "./useArchivedThreadSnapshots";

export function ArchivedThreadsRouteScreen() {
const { expand } = useClerkSettingsSheetDetent();
const { savedConnectionsById } = useSavedRemoteConnections();
const [searchQuery, setSearchQuery] = useState("");
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<EnvironmentId | null>(null);
Expand Down Expand Up @@ -70,9 +68,8 @@ export function ArchivedThreadsRouteScreen() {

useFocusEffect(
useCallback(() => {
expand();
refresh();
}, [expand, refresh]),
}, [refresh]),
);

return (
Expand Down
44 changes: 0 additions & 44 deletions apps/mobile/src/features/cloud/ClerkSettingsSheetDetent.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions apps/mobile/src/features/cloud/connectOnboardingNavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { appAtomRegistry } from "../../state/atom-registry";
import { clearConnectOnboardingRequest, connectOnboardingRequestAtom } from "./connectOnboarding";
import { isConnectOnboardingOptedOut } from "./connectOnboardingOptOut";

// Sign-in happens inside the Settings sheet; give its detent/session-state
// transitions a beat to settle before presenting another formSheet on top.
// Sign-in happens inside the Settings sheet; give its session-state transition
// a beat to settle before presenting another formSheet on top.
const PRESENT_ONBOARDING_DELAY_MS = 600;

/**
Expand Down
24 changes: 20 additions & 4 deletions apps/mobile/src/features/home/HomeRouteScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,17 @@ export function HomeRouteScreen() {
onOwnershipRelationChange={setOwnershipRelation}
onHideSettledThreadsChange={setHideSettledThreads}
onOpenEnvironments={() =>
navigation.navigate("SettingsSheet", { screen: "SettingsEnvironments" })
navigation.navigate("SettingsSheet", {
screen: "SettingsContent",
params: { screen: "SettingsEnvironments" },
})
}
onOpenSettings={() =>
navigation.navigate("SettingsSheet", {
screen: "SettingsContent",
params: { screen: "Settings" },
})
}
onOpenSettings={() => navigation.navigate("SettingsSheet", { screen: "Settings" })}
onProjectSortOrderChange={setProjectSortOrder}
onSearchQueryChange={setSearchQuery}
onStartNewTask={() => navigation.navigate("NewTaskSheet", { screen: "NewTask" })}
Expand All @@ -228,7 +236,10 @@ export function HomeRouteScreen() {
threadGrouping={listOptions.threadGrouping}
hideSettledThreads={hideSettledThreads}
onAddConnection={() =>
navigation.navigate("SettingsSheet", { screen: "SettingsEnvironmentNew" })
navigation.navigate("SettingsSheet", {
screen: "SettingsContent",
params: { screen: "SettingsEnvironmentNew" },
})
}
onArchiveThread={archiveThread}
onDeleteThread={confirmDeleteThread}
Expand All @@ -242,7 +253,12 @@ export function HomeRouteScreen() {
onToggleEnvironment={toggleSelectedEnvironmentId}
onMovePinnedThread={movePinnedThread}
onProjectChange={setSelectedProjectKey}
onOpenSettings={() => navigation.navigate("SettingsSheet", { screen: "Settings" })}
onOpenSettings={() =>
navigation.navigate("SettingsSheet", {
screen: "SettingsContent",
params: { screen: "Settings" },
})
}
onProjectSortOrderChange={setProjectSortOrder}
onSearchQueryChange={setSearchQuery}
onSelectThread={(thread) => {
Expand Down
10 changes: 8 additions & 2 deletions apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -503,13 +503,19 @@ function AdaptiveWorkspaceLayoutContent(
);

const handleOpenSettings = useCallback(() => {
navigation.navigate("SettingsSheet", { screen: "Settings" });
navigation.navigate("SettingsSheet", {
screen: "SettingsContent",
params: { screen: "Settings" },
});
}, [navigation]);

// Minted here (root stack navigation) so the sidebar pane stays free of
// navigation hooks — on iOS it renders inside an independent nav tree.
const handleOpenEnvironmentSettings = useCallback(() => {
navigation.navigate("SettingsSheet", { screen: "SettingsEnvironments" });
navigation.navigate("SettingsSheet", {
screen: "SettingsContent",
params: { screen: "SettingsEnvironments" },
});
}, [navigation]);

const handleNewThreadInProject = useCallback(
Expand Down
33 changes: 16 additions & 17 deletions apps/mobile/src/features/settings/SettingsAuthRouteScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { useAuth } from "@clerk/expo";
import { AuthView, UserProfileView } from "@clerk/expo/native";
import { StackActions, useNavigation } from "@react-navigation/native";
import { NativeStackScreenOptions } from "../../native/StackHeader";
import { useCallback, useEffect } from "react";
import { useCallback, useLayoutEffect } from "react";
import { View } from "react-native";

import { hasCloudPublicConfig } from "../cloud/publicConfig";

export function SettingsAuthRouteScreen() {
const navigation = useNavigation();

useEffect(() => {
useLayoutEffect(() => {
if (!hasCloudPublicConfig()) {
navigation.dispatch(StackActions.replace("Settings"));
navigation.dispatch(StackActions.replace("SettingsContent"));
}
}, [navigation]);

Expand All @@ -22,20 +21,20 @@ export function SettingsAuthRouteScreen() {
function ConfiguredSettingsAuthRouteScreen() {
const { isLoaded, isSignedIn } = useAuth({ treatPendingAsSignedOut: false });
const navigation = useNavigation();
const handleHostBack = useCallback(() => navigation.goBack(), [navigation]);
const handleHostBack = useCallback(
() => navigation.dispatch(StackActions.popTo("SettingsContent")),
[navigation],
);

return (
<>
<NativeStackScreenOptions options={{ headerShown: false }} />
<View collapsable={false} className="flex-1 overflow-hidden bg-sheet">
{isLoaded ? (
isSignedIn ? (
<UserProfileView isDismissible={false} onHostBack={handleHostBack} />
) : (
<AuthView isDismissible={false} onHostBack={handleHostBack} />
)
) : null}
</View>
</>
<View collapsable={false} className="flex-1 overflow-hidden bg-sheet">
{isLoaded ? (
isSignedIn ? (
<UserProfileView isDismissible={false} onHostBack={handleHostBack} />
) : (
<AuthView isDismissible={false} onHostBack={handleHostBack} />
)
) : null}
</View>
);
}
Loading
Loading