Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5cc99e1
fix(desktop): agent clicks in the browser no longer pop Save dialogs …
t3dotgg Oct 1, 2026
8dc07f1
chore(mobile): upgrade to Expo SDK 58 and React Native 0.88 RC (#12045)
juliusmarminge Oct 1, 2026
e41755c
feat(mobile): render the Android subscription widget with expo-widget…
juliusmarminge Oct 1, 2026
09388bf
feat(mobile): degrade the agent Live Activity once its content goes s…
juliusmarminge Oct 1, 2026
438af29
feat(notifications): stack agent alerts by thread on both platforms (…
juliusmarminge Oct 1, 2026
ce920f2
chore(mobile): drive dev-menu suppression from the dev-client launch …
juliusmarminge Oct 1, 2026
9962e69
fix(mobile): read display scale and width from the view's scene, not …
juliusmarminge Oct 1, 2026
1e9c360
refactor(mobile): adopt Expo Modules 2.0 for function-only native mem…
juliusmarminge Oct 1, 2026
bed69f6
feat(mobile): suppress only the on-screen thread's alert on Android (…
juliusmarminge Oct 1, 2026
b91e466
feat(marketing): replace the screenshot crop with a designed social c…
flamboh Oct 1, 2026
a3fb539
docs: update user count in AGENTS.md (#11413)
Kamkmgamer Oct 1, 2026
5a574a7
test(web): remove duplicate sidebar ordering tests (#14558)
t3-code[bot] Oct 1, 2026
a97a4a9
Update marketing stats for GitHub stars and users
juliusmarminge Oct 1, 2026
6ea01f8
fix: cloned projects show their favicon instead of a monogram (#14635)
t3dotgg Oct 1, 2026
094fb23
docs: server features are services, and handlers stay thin (#14613)
juliusmarminge Oct 1, 2026
a3abb52
fix(ci): pin eas-cli so mobile PR previews deploy again (#14659)
juliusmarminge Oct 1, 2026
10ac2f2
fix(server): background GitHub polling uses ~74% fewer calls with bat…
t3dotgg Oct 1, 2026
67f6400
fix(web): show the Docker icon on dockerfile code blocks (#14675)
chrisdeeming Oct 1, 2026
0a04cc5
feat(web): update providers on every machine with one click (#14678)
t3dotgg Oct 2, 2026
b33eda1
feat(web): beta Working section hides busy threads until they need yo…
t3dotgg Oct 2, 2026
ced865c
fix(web): PR checks status collapses to its icon instead of wrapping …
flamboh Oct 2, 2026
fa6262d
fix: remove outdated restart setting update advice (#14710)
t3-code[bot] Oct 2, 2026
20012eb
fix(web): keep the terminal toggle clear of the last header action (#…
Mnigos Oct 2, 2026
2731929
fix(web): group composer undo the way the Lexical composer did (#14674)
chrisdeeming Oct 2, 2026
3e60fec
fix(desktop): markdown pages no longer render invisible in the dark-m…
shivamhwp Oct 2, 2026
39405f1
fix(mobile): one No project row at the top of the project picker (#14…
juliusmarminge Oct 2, 2026
99e0852
Remove unused isCloudDebugEnabled and isTerminalDebugEnabled (#14367)
kridaydave Oct 2, 2026
54084ae
fix(mobile): update Expo 58 to restore widgets and Live Activities (#…
juliusmarminge Oct 2, 2026
ce96e4c
fix(mobile): project picker cards match the settings card color (#14756)
juliusmarminge Oct 2, 2026
4804036
chore(mobile): bump app version to 1.4.0
t3-code[bot] Oct 2, 2026
6c8fed3
fix(markdown): keep Windows paths intact in link and image destinatio…
SunkenInTime Oct 2, 2026
3d45bd9
chore(v2): merge main into the V2 branch (15 commits)
juliusmarminge Oct 2, 2026
af7de21
test(web): import the drop helpers the Working shelf tests use
juliusmarminge Oct 2, 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
16 changes: 15 additions & 1 deletion apps/desktop/src/preview/Manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,7 @@ describe("PreviewManager", () => {
),
);

effectIt.effect("emulates prefers-color-scheme and re-applies it across webview swaps", () =>
effectIt.effect("re-applies opaque base and color scheme across webview swaps", () =>
withManager((manager) =>
Effect.gen(function* () {
const makeWebContents = (id: number) => {
Expand Down Expand Up @@ -1650,6 +1650,16 @@ describe("PreviewManager", () => {
yield* manager.registerWebview("tab_scheme", 42);
yield* Effect.yieldNow;

// Guests start with a transparent base; dark-scheme pages need an opaque
// one or Chromium skips their dark canvas.
const opaqueBase = {
color: { r: 255, g: 255, b: 255, a: 1 },
};
expect(first.sendCommand).toHaveBeenCalledWith(
"Emulation.setDefaultBackgroundColorOverride",
opaqueBase,
);

yield* manager.setColorScheme("tab_scheme", "dark");

expect(first.sendCommand).toHaveBeenCalledWith("Emulation.setEmulatedMedia", {
Expand All @@ -1662,6 +1672,10 @@ describe("PreviewManager", () => {
yield* manager.registerWebview("tab_scheme", 43);
yield* Effect.yieldNow;

expect(replacement.sendCommand).toHaveBeenCalledWith(
"Emulation.setDefaultBackgroundColorOverride",
opaqueBase,
);
expect(replacement.sendCommand).toHaveBeenCalledWith("Emulation.setEmulatedMedia", {
features: [{ name: "prefers-color-scheme", value: "dark" }],
});
Expand Down
13 changes: 13 additions & 0 deletions apps/desktop/src/preview/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,19 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function
),
{ concurrency: "unbounded", discard: true },
);
// Electron gives `<webview>` guests a transparent base background, and
// Chromium only paints a dark canvas for dark color-scheme pages over an
// opaque base. Without this, dark-scheme pages with no background of
// their own (text/plain, e.g. .md files) render white text on white.
// White matches the webview's existing white backing, so light pages look
// the same; Chromium still swaps in its dark canvas for dark-scheme pages.
yield* attemptPromise(
{ operation: "initializeDebugger.defaultBackground", webContentsId: wc.id },
() =>
wcDebugger.sendCommand("Emulation.setDefaultBackgroundColorOverride", {
color: { r: 255, g: 255, b: 255, a: 1 },
}),
);
return [
control,
replaceMap(sessions, (copy) => {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const clientSettings: ClientSettings = {
sidebarThreadSortOrder: "created_at",
sidebarThreadPreviewCount: 6,
legacySidebarEnabled: false,
sidebarWorkingShelfEnabled: false,
loadBalancingEnabled: false,
loadBalancingWeights: { "environment-1": 75, "environment-2": 0 },
pullRequestMergeMethodOverrides: {},
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 @@ -229,7 +229,7 @@ const config: ExpoConfig = {
slug: "t3-code",
platforms: ["ios", "android"],
scheme: variant.scheme,
version: "1.3.1",
version: "1.4.0",
runtimeVersion: {
// Development manifests resolve on every launch, so avoid fingerprint's
// expensive native-project calculation there. Preview and production stay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
nativeMarkdownChunkSpacing,
nativeMarkdownDocumentChunks,
nativeMarkdownDocumentRuns,
nativeMarkdownWithAuthoredWindowsPaths,
nativeMarkdownWithPreservedSoftBreaks,
} from "./nativeMarkdownText";
import { MarkdownImageRendererContext, NativeMarkdownBlock } from "./NativeMarkdownBlock";
Expand Down Expand Up @@ -51,11 +52,10 @@ export function SelectableMarkdownText({
marginBottom = 0,
}: SelectableMarkdownTextProps) {
const chunks = useMemo(() => {
const parsedDocument = parseMarkdownWithOptions(markdown, {
gfm: true,
html: true,
math: false,
});
const parsedDocument = nativeMarkdownWithAuthoredWindowsPaths(
parseMarkdownWithOptions(markdown, { gfm: true, html: true, math: false }),
markdown,
);
const document = preserveSoftBreaks
? nativeMarkdownWithPreservedSoftBreaks(parsedDocument)
: parsedDocument;
Expand Down
30 changes: 30 additions & 0 deletions apps/mobile/modules/t3-markdown-text/src/nativeMarkdownText.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { MarkdownNode } from "react-native-nitro-markdown/headless";
import { collectComposerInlineTokens } from "@t3tools/shared/composerInlineTokens";
import { imageMimeType } from "@t3tools/shared/image";
import { isWindowsAbsolutePath } from "@t3tools/shared/path";
import { videoMimeType } from "@t3tools/shared/video";
/**
* Every accent shares a lightness so no kind reads heavier than another; only hue carries
Expand Down Expand Up @@ -519,6 +520,35 @@ export function nativeMarkdownWithPreservedSoftBreaks(node: MarkdownNode): Markd
};
}

const WINDOWS_DESTINATION_PATTERN = /\](?:\(|:)\s*<?((?:[A-Za-z]:|\\\\)(?:\\.|[^\s()<>\\])*)/g;
const MARKDOWN_ESCAPE_PATTERN = /\\([!-/:-@[-`{-~])/g;

/**
* md4c reads the `\.` in `C:\me\.t3\shot.png` as an escape, even in a link destination.
* Every backslash in a Windows path is a separator, so link and image paths go back to
* the destination as written in `markdown`. A parsed path that more than one written
* destination could have produced stays as parsed.
*/
export function nativeMarkdownWithAuthoredWindowsPaths(
node: MarkdownNode,
markdown: string,
): MarkdownNode {
const authoredByParsed = new Map<string, string | null>();
for (const [, authored = ""] of markdown.matchAll(WINDOWS_DESTINATION_PATTERN)) {
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
if (!isWindowsAbsolutePath(authored)) continue;
const parsed = authored.replace(MARKDOWN_ESCAPE_PATTERN, "$1");
const known = authoredByParsed.get(parsed);
authoredByParsed.set(parsed, known === undefined || known === authored ? authored : null);
}
if (authoredByParsed.size === 0) return node;
const restore = (current: MarkdownNode): MarkdownNode => {
const href = current.href && authoredByParsed.get(current.href);
const children = current.children?.map(restore);
return { ...current, ...(href ? { href } : {}), ...(children ? { children } : {}) };
};
return restore(node);
}

function appendBlockTerminator(
runs: NativeMarkdownTextRun[],
context: RunContext,
Expand Down
67 changes: 34 additions & 33 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"@clerk/expo": "catalog:",
"@effect/atom-react": "catalog:",
"@expo-google-fonts/dm-sans": "^0.4.2",
"@expo/metro-runtime": "~58.0.9",
"@expo/ui": "~58.0.9",
"@expo/metro-runtime": "~58.0.10",
"@expo/ui": "~58.0.11",
"@legendapp/list": "catalog:",
"@material/material-color-utilities": "0.3.0",
"@noble/curves": "catalog:",
Expand All @@ -74,41 +74,41 @@
"clsx": "^2.1.1",
"diff": "8.0.3",
"effect": "catalog:",
"expo": "58.0.0",
"expo-asset": "~58.0.9",
"expo-audio": "58.0.3",
"expo-auth-session": "~58.0.6",
"expo-blur": "58.0.2",
"expo": "58.0.2",
"expo-asset": "~58.0.10",
"expo-audio": "58.0.4",
"expo-auth-session": "~58.0.7",
"expo-blur": "58.0.3",
"expo-build-properties": "~58.0.9",
"expo-camera": "~58.0.5",
"expo-clipboard": "~58.0.2",
"expo-camera": "~58.0.7",
"expo-clipboard": "~58.0.3",
"expo-constants": "~58.0.9",
"expo-crypto": "~58.0.3",
"expo-dev-client": "~58.0.9",
"expo-device": "~58.0.3",
"expo-document-picker": "~58.0.3",
"expo-file-system": "~58.0.4",
"expo-font": "~58.0.4",
"expo-crypto": "~58.0.4",
"expo-dev-client": "~58.0.10",
"expo-device": "~58.0.5",
"expo-document-picker": "~58.0.4",
"expo-file-system": "~58.0.5",
"expo-font": "~58.0.5",
"expo-glass-effect": "58.0.3",
"expo-haptics": "~58.0.3",
"expo-image": "~58.0.8",
"expo-image-manipulator": "~58.0.10",
"expo-image-picker": "~58.0.9",
"expo-keep-awake": "~58.0.2",
"expo-linking": "~58.0.9",
"expo-network": "~58.0.2",
"expo-notifications": "~58.0.9",
"expo-haptics": "~58.0.4",
"expo-image": "~58.0.10",
"expo-image-manipulator": "~58.0.11",
"expo-image-picker": "~58.0.10",
"expo-keep-awake": "~58.0.3",
"expo-linking": "~58.0.10",
"expo-network": "~58.0.3",
"expo-notifications": "~58.0.11",
"expo-paste-input": "^0.1.15",
"expo-quick-actions": "^6.0.2",
"expo-secure-store": "~58.0.1",
"expo-sharing": "58.0.12",
"expo-splash-screen": "~58.0.2",
"expo-sqlite": "~58.0.7",
"expo-secure-store": "~58.0.2",
"expo-sharing": "58.0.13",
"expo-splash-screen": "~58.0.4",
"expo-sqlite": "~58.0.8",
"expo-symbols": "~58.0.3",
"expo-updates": "~58.0.11",
"expo-video": "~58.0.5",
"expo-web-browser": "~58.0.3",
"expo-widgets": "58.0.9",
"expo-updates": "~58.0.13",
"expo-video": "~58.0.6",
"expo-web-browser": "~58.0.4",
"expo-widgets": "58.0.11",
"react": "19.3.0",
"react-dom": "19.3.0",
"react-native": "0.88.0-rc.3",
Expand All @@ -133,14 +133,15 @@
"@pierre/trees": "1.0.0-beta.4",
"@types/react": "~19.3.0",
"@types/react-dom": "~19.2.3",
"babel-preset-expo": "~58.0.6",
"babel-preset-expo": "~58.0.8",
"tailwindcss": "^4.0.0",
"typescript": "catalog:"
},
"expo": {
"install": {
"exclude": [
"react-native-keyboard-controller"
"react-native-keyboard-controller",
"typescript"
]
},
"autolinking": {
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ function ArchivedThreadRow(props: {
readonly thread: EnvironmentThreadShell;
}) {
const { width: windowWidth } = useWindowDimensions();
const cardColor = useUniwindTheme()["--color-card"];
const cardColor = useUniwindTheme()["--color-grouped-card"];
const timestamp = relativeTime(props.thread.archivedAt ?? props.thread.updatedAt);
const subtitle = [props.environmentLabel, props.thread.branch].filter((part): part is string =>
Boolean(part),
Expand Down Expand Up @@ -224,7 +224,7 @@ function ArchivedThreadRow(props: {
>
{() => (
<View
className={`flex-row items-center gap-3 bg-card px-4 py-3 ${props.isLast ? "" : "border-b border-separator"}`}
className={`flex-row items-center gap-3 bg-grouped-card px-4 py-3 ${props.isLast ? "" : "border-b border-separator"}`}
>
<View className="h-[34px] w-[34px] items-center justify-center rounded-[11px] bg-subtle">
<SymbolView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ function ConfiguredConnectOnboardingRouteScreen() {
showHeader={false}
/>
) : (
<View collapsable={false} className="rounded-[24px] bg-card p-5">
<View collapsable={false} className="rounded-[24px] bg-grouped-card p-5">
<Text className="text-sm leading-normal text-foreground-muted">
Sign in to your T3 account to set up T3 Connect.
</Text>
Expand Down
4 changes: 0 additions & 4 deletions apps/mobile/src/features/cloud/cloudDebugLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ const logger = createDebugLogger("cloud", {
legacyGlobalFlag: "__T3_CLOUD_DEBUG__",
});

export function isCloudDebugEnabled(): boolean {
return logger.isEnabled();
}

export function cloudDebugLog(event: string, data?: Record<string, unknown>): void {
logger.log(event, data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export function ConnectionsNewRouteScreen({
/>
</View>
) : (
<View className="items-center gap-3 rounded-[24px] border-continuous bg-card px-5 py-8">
<View className="items-center gap-3 rounded-[24px] border-continuous bg-grouped-card px-5 py-8">
<Text className="text-center text-sm leading-normal text-foreground-muted">
Camera permission is required to scan a QR code.
</Text>
Expand All @@ -242,7 +242,7 @@ export function ConnectionsNewRouteScreen({
</View>
)
) : (
<View collapsable={false} className="gap-4 rounded-[24px] bg-card p-4">
<View collapsable={false} className="gap-4 rounded-[24px] bg-grouped-card p-4">
<ConnectionFormField
label="Host"
autoCapitalize="none"
Expand Down
11 changes: 6 additions & 5 deletions apps/mobile/src/features/projects/AddProjectScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ function ListSection(props: { readonly children: ReactNode }) {
<View
className={
Platform.OS === "android"
? "overflow-hidden rounded-[28px] bg-card"
: "overflow-hidden rounded-[24px] bg-card"
? "overflow-hidden rounded-[28px] bg-grouped-card"
: "overflow-hidden rounded-[24px] bg-grouped-card"
}
>
{props.children}
Expand All @@ -197,6 +197,7 @@ function ListRow(props: {
if (Platform.OS === "android") {
return (
<MaterialListRow
className="bg-grouped-card"
title={props.title}
subtitle={props.subtitle}
leading={props.icon}
Expand All @@ -213,7 +214,7 @@ function ListRow(props: {
disabled={props.disabled}
onPress={props.onPress}
className={cn(
"bg-card px-3.5 py-2.5 active:opacity-70",
"bg-grouped-card px-3.5 py-2.5 active:opacity-70",
!props.isFirst && "border-t border-border-subtle",
props.disabled && "opacity-[0.45]",
)}
Expand Down Expand Up @@ -448,7 +449,7 @@ function EmptyEnvironmentState() {
const navigation = useNavigation();

return (
<View className="items-center gap-3 rounded-2xl bg-card px-5 py-8">
<View className="items-center gap-3 rounded-2xl bg-grouped-card px-5 py-8">
<Text className="text-center text-lg font-t3-bold">Environment unavailable</Text>
<Text className="text-center text-sm leading-normal text-foreground-muted">
Start or reconnect an environment before adding a project.
Expand Down Expand Up @@ -1332,7 +1333,7 @@ export function AddProjectDestinationScreen(props: {
<AddProjectShell title="Clone destination">
{error ? <ErrorBanner message={error} /> : null}
{repositoryTitle ? (
<View className="rounded-[24px] bg-card px-4 py-3">
<View className="rounded-[24px] bg-grouped-card px-4 py-3">
<Text className="text-base font-t3-bold">{repositoryTitle}</Text>
<Text className="mt-0.5 text-xs text-foreground-muted" numberOfLines={2}>
{remoteUrl}
Expand Down
4 changes: 0 additions & 4 deletions apps/mobile/src/features/terminal/terminalDebugLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ const logger = createDebugLogger("terminal", {
legacyGlobalFlag: "__T3_TERMINAL_DEBUG__",
});

export function isTerminalDebugEnabled(): boolean {
return logger.isEnabled();
}

export function terminalDebugLog(message: string, data?: Record<string, unknown>): void {
logger.log(message, data);
}
Loading
Loading