Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cad2c93
feat: multi-provider pull requests page with in-app reviews (#4849)
Bil0000 Aug 10, 2026
8de0aa2
perf(desktop): probe the Windows shell environment concurrently (#5878)
tsouth89 Aug 10, 2026
b91a000
feat(web): add duplicate action to the T3 Code default theme (#6013)
UtkarshUsername Aug 10, 2026
0a7c662
fix(web): improve built-in theme contrast (#6000)
chrisdeeming Aug 10, 2026
1b120f3
fix(ci): extend release publish timeout (#6034)
t3-code[bot] Aug 10, 2026
428d9f9
Allow Android tablets to rotate (#5613)
juliusmarminge Aug 10, 2026
a7b0366
fix(web): account for Windows window controls in PR page header (#6049)
UtkarshUsername Aug 10, 2026
9afef94
feat: add three-hour snooze option (#5914)
Noojuno Aug 10, 2026
e5c82d7
fix(mobile): keep chat composer above the Android gesture bar (#5988)
PollyGlot Aug 11, 2026
2ea51bd
fix(server): OpenCode model parsing drops models with a slash in the …
arhxam Aug 11, 2026
59d0c92
fix(web): persist sidebar shelf collapse state (#5136)
PixPMusic Aug 11, 2026
f973097
perf(web): skip base64 for oversized image candidates (#5220)
tarik02 Aug 11, 2026
1fa315e
fix(server): skip Linux libc detection on Windows/macOS (#5354)
bkntr Aug 11, 2026
c14bcca
fix(server): advertise 256-color TERM on Windows terminals (#5693)
yassiEmp Aug 11, 2026
9c7622d
fix(server): handle unborn HEAD in VCS status (#5944)
t3-code[bot] Aug 11, 2026
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
2db0845
fix(web): use upload icon for disabled push action (#6207)
t3-code[bot] Aug 11, 2026
f0b57ca
feat(web): add Open VSX theme search (#5654)
t3-code[bot] Aug 11, 2026
c196f42
fix(web): clean up composer resize animation (#6209)
extoci Aug 11, 2026
52e5a75
feat(web): compact sidebar footer actions (#6210)
maria-rcks Aug 11, 2026
560d4a4
fix(web): keep sidebar wordmark visible at minimum width (#6246)
extoci Aug 11, 2026
d37a9b0
feat(mobile): add thread title regeneration (#6253)
chrisdeeming Aug 12, 2026
63e6fae
chore: add dara to vouched (#6259)
maria-rcks Aug 12, 2026
5a84614
fix(web): align the composer model picker (#6252)
t3-code[bot] Aug 12, 2026
8b4233a
chore: sync upstream main
tarik02 Aug 12, 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
1 change: 1 addition & 0 deletions .github/VOUCHED.td
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ github:shivamhwp
github:jappyjan
github:justsomelegs
github:UtkarshUsername
github:SunkenInTime
93 changes: 83 additions & 10 deletions .github/workflows/mobile-eas-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ name: Mobile EAS Production
#
# Every merge to main that touches the mobile app reconciles, per platform:
# 1. Store builds: if the latest production build's version differs from
# app.config.ts, cut a new build with --auto-submit (TestFlight +
# Play internal track). Bumping `version` is therefore all it takes to
# app.config.ts, cut a new build and submit it (TestFlight + Play internal
# track). Bumping `version` is therefore all it takes to
# start the next release train — the first build of a version enters
# external-TestFlight beta review immediately, and later builds of the
# same version auto-approve. Releasing to the App Store stays a manual
# App Store Connect step.
# same version auto-approve until that version is released. After App
# Store approval, Apple closes the release train and `version` must be
# bumped before another iOS build can be submitted. Releasing to the App
# Store stays a manual App Store Connect step.
# 2. OTA: publish a production-channel update for each platform where at
# least one finished production build matches the current native
# fingerprint. Old-version binaries with a matching fingerprint receive
Expand Down Expand Up @@ -42,6 +44,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 only)"
required: false
Expand Down Expand Up @@ -89,11 +95,21 @@ 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:
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 Expand Up @@ -135,6 +151,65 @@ jobs:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
run: eas env:pull production --non-interactive

- name: Apply manual version override
if: steps.version_app_token.outcome == 'success'
env:
GH_TOKEN: ${{ steps.version_app_token.outputs.token }}
APP_SLUG: ${{ steps.version_app_token.outputs.app-slug }}
RELEASE_VERSION: ${{ inputs.version }}
run: |
if [ "$GITHUB_REF_TYPE" != "branch" ]; then
echo "Version overrides require dispatching this workflow from a branch; received $GITHUB_REF_TYPE '$GITHUB_REF_NAME'." >&2
exit 1
fi
if ! [[ "$RELEASE_VERSION" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]]; then
echo "Version override must contain two or three dot-separated integers; received '$RELEASE_VERSION'." >&2
exit 1
fi

node --input-type=module -e '
import fs from "node:fs";
const path = "apps/mobile/app.config.ts";
const source = fs.readFileSync(path, "utf8");
const next = source.replace(
/^( version: ")[^"]+(".*)$/m,
`$1${process.env.RELEASE_VERSION}$2`,
);
if (next === source && !source.includes(` version: "${process.env.RELEASE_VERSION}"`)) {
throw new Error("Could not update app version");
}
fs.writeFileSync(path, next);
'
vp fmt apps/mobile/app.config.ts

if git diff --quiet -- apps/mobile/app.config.ts; then
echo "app.config.ts is already at $RELEASE_VERSION; no version commit needed."
exit 0
fi

user_id="$(gh api "/users/${APP_SLUG}[bot]" --jq .id)"
git config user.name "${APP_SLUG}[bot]"
git config user.email "${user_id}+${APP_SLUG}[bot]@users.noreply.github.com"
git add apps/mobile/app.config.ts
git commit \
-m "chore(mobile): bump app version to $RELEASE_VERSION" \
-m "Co-authored-by: codex <codex@users.noreply.github.com>"
git push origin "HEAD:refs/heads/${GITHUB_REF_NAME}"

- name: Summarize manual build version
if: steps.expo-token.outputs.present == 'true' && github.event_name == 'workflow_dispatch' && inputs.mode == 'build'
working-directory: apps/mobile
run: |
version="$(npx expo config --json --type public | jq -r '.version')"
{
echo "## Manual production build"
echo
echo "- App version: \`$version\`"
echo "- Platform: \`${{ inputs.platform }}\`"
echo
echo "> Apple closes an iOS release train after App Store approval. Before building iOS, confirm \`$version\` is newer than the approved App Store version."
} >> "$GITHUB_STEP_SUMMARY"

- name: Build and submit (manual)
if: steps.expo-token.outputs.present == 'true' && github.event_name == 'workflow_dispatch' && inputs.mode == 'build'
working-directory: apps/mobile
Expand All @@ -157,10 +232,8 @@ jobs:

# No --status filter on build:list: an in-queue/in-progress build must
# count as existing, or every merge during the build window would cut a
# duplicate. Builds started here stay attached to this serialized run so
# the queued run for a later merge cannot overtake them and lose its OTA.
# After an errored build, retry via workflow_dispatch mode=build — pushes
# won't re-trigger it until the app version changes.
# duplicate. After an errored build, retry via workflow_dispatch
# mode=build — pushes won't re-trigger it until the app version changes.
- id: store_builds
name: Ensure store builds exist for the current app version
if: steps.expo-token.outputs.present == 'true' && github.event_name == 'push'
Expand All @@ -178,8 +251,8 @@ jobs:
continue
fi
echo "$platform: latest production build is $latest, app.config.ts says $version — building"
if eas build --platform "$platform" --profile production --auto-submit --non-interactive; then
echo ":building_construction: $platform: cut production build for $version (auto-submitted)" >> "$GITHUB_STEP_SUMMARY"
if eas build --platform "$platform" --profile production --auto-submit --non-interactive --no-wait; then
echo ":building_construction: $platform: scheduled production build and submission for $version" >> "$GITHUB_STEP_SUMMARY"
else
failed=1
echo ":x: $platform: production build or submission failed for $version" >> "$GITHUB_STEP_SUMMARY"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -843,8 +843,8 @@ jobs:

release:
name: Publish GitHub Release
needs: [preflight, build, build_web_dist]
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' && needs.build_web_dist.result == 'success' }}
needs: [preflight, build, build_web_dist, publish_cli]
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' && needs.build_web_dist.result == 'success' && needs.publish_cli.result == 'success' }}
Comment on lines +846 to +847

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the disabled CLI gate from releases

The publish_cli job is explicitly disabled with if: false at line 782, so its result is always skipped, never success. Requiring needs.publish_cli.result == 'success' therefore skips the GitHub release job for every stable, nightly, and canary run, preventing release artifacts from being published; keep the release independent of this fork-disabled job or explicitly accept a skipped result.

AGENTS.md reference: AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
Expand Down
16 changes: 12 additions & 4 deletions apps/desktop/src/shell/DesktopShellEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,18 @@ const installWindowsEnvironment = Effect.fn("desktop.shellEnvironment.installWin
function* (
config: ShellEnvironmentConfig,
): Effect.fn.Return<void, never, ChildProcessSpawner.ChildProcessSpawner> {
const noProfile = yield* readWindowsEnvironment(["PATH"], { loadProfile: false });
const profile = yield* readWindowsEnvironment(WINDOWS_PROFILE_ENV_NAMES, {
loadProfile: true,
});
// Concurrent, not sequential: these two probes are independent (only their
// results are combined below) and each spawns its own PowerShell. Run in
// series they sit at offset 0 of desktop.startup, before anything else, and
// launch traces measured them at 2718ms then 2066ms — the entire 4.8s
// startup span, of which desktop.bootstrap is ~30ms.
const [noProfile, profile] = yield* Effect.all(
[
readWindowsEnvironment(["PATH"], { loadProfile: false }),
readWindowsEnvironment(WINDOWS_PROFILE_ENV_NAMES, { loadProfile: true }),
],
{ concurrency: 2 },
);
const mergedPath = mergePaths("win32", [
trimNonEmpty(profile.PATH),
trimNonEmpty(knownWindowsCliDirs(config.env).join(";")),
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ const config: ExpoConfig = {
slug: "t3-code",
platforms: ["ios", "android"],
scheme: variant.scheme,
version: "1.0.2",
version: "1.0.3",
runtimeVersion: {
// Fingerprint (not appVersion) so an OTA only reaches binaries whose native
// project — native deps, config plugins, AND patches/ — matches the update.
Expand Down Expand Up @@ -338,6 +338,7 @@ const config: ExpoConfig = {
"./plugins/withAndroidModernPopupMenu.cjs",
"./plugins/withAndroidModernAlertDialog.cjs",
"./plugins/withAndroidPredictiveBackCompat.cjs",
"./plugins/withAndroidTabletOrientation.cjs",
...(isIosPersonalTeamBuild ? ["./plugins/withoutIosPersonalTeamCapabilities.cjs"] : []),
],
extra: {
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,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
80 changes: 80 additions & 0 deletions apps/mobile/plugins/withAndroidTabletOrientation.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const { withMainActivity } = require("expo/config-plugins");

// The top-level `orientation: "portrait"` writes android:screenOrientation="portrait"
// into the manifest, which locks every Android device — including tablets — to
// portrait. iOS doesn't have this problem: iPads must support all orientations
// because the app is multitasking-capable, so only iPhones end up portrait-only.
// Mirror that split on Android: keep the manifest lock for phones and lift it at
// runtime on tablets (smallest width >= 600dp, the standard tablet breakpoint),
// since requestedOrientation set at runtime overrides the manifest value.
// FULL_USER allows all four orientations while still respecting the user's
// auto-rotate lock, matching iPad behavior. Foldables change
// smallestScreenWidthDp on fold/unfold without recreating the activity
// (smallestScreenSize is in the manifest's configChanges), so the policy is
// re-evaluated in onConfigurationChanged: unfolding past the tablet breakpoint
// unlocks rotation, and folding back restores the portrait lock.

const ORIENTATION_METHODS = `
// Applied in onCreate and re-applied on fold/unfold; added by
// withAndroidTabletOrientation.
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
applyTabletOrientation()
}

private fun applyTabletOrientation() {
requestedOrientation = if (resources.configuration.smallestScreenWidthDp >= 600) {
ActivityInfo.SCREEN_ORIENTATION_FULL_USER
} else {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
`;

const ORIENTATION_ON_CREATE_CALL = `
applyTabletOrientation()`;

function insertAfter(contents, anchor, insertion, description) {
const index = contents.indexOf(anchor);
if (index === -1) {
throw new Error(
`withAndroidTabletOrientation: could not find ${description} in MainActivity — the Expo template changed; update the plugin anchors.`,
);
}
const end = index + anchor.length;
return contents.slice(0, end) + insertion + contents.slice(end);
}

module.exports = function withAndroidTabletOrientation(config) {
return withMainActivity(config, (nextConfig) => {
let contents = nextConfig.modResults.contents;
if (nextConfig.modResults.language !== "kt") {
throw new Error("withAndroidTabletOrientation: MainActivity must be Kotlin.");
}
if (contents.includes("SCREEN_ORIENTATION_FULL_USER")) {
return nextConfig;
}

contents = insertAfter(
contents,
"import android.os.Bundle",
"\nimport android.content.pm.ActivityInfo\nimport android.content.res.Configuration",
"the android.os.Bundle import",
);
contents = insertAfter(
contents,
"class MainActivity : ReactActivity() {",
ORIENTATION_METHODS,
"the MainActivity class declaration",
);
contents = insertAfter(
contents,
"super.onCreate(null)",
ORIENTATION_ON_CREATE_CALL,
"the super.onCreate call",
);

nextConfig.modResults.contents = contents;
return nextConfig;
});
};
33 changes: 20 additions & 13 deletions apps/mobile/src/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { AppText as Text } from "./components/AppText";
import { getCompactBrandHeaderOptions } from "./components/CompactBrandTitle";
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
Loading
Loading