Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
76da264
perf(mobile): highlight code with native @pierre/highlights instead o…
juliusmarminge Oct 24, 2026
7fd2e09
fix(desktop): support Linux distro protocol launchers (#24591)
juliusmarminge Oct 24, 2026
c4fa462
fix(provider-acp): force-kill terminal commands that ignore SIGTERM (…
juliusmarminge Oct 24, 2026
843f4e3
fix(grok): invoke leading picked skills through native commands (#24605)
juliusmarminge Oct 24, 2026
553203c
fix(server): long agent turns no longer load whole when a thread open…
t3dotgg Oct 24, 2026
8bcef34
fix(server): analytics stop counting reconnects, retries, and dev ser…
t3dotgg Oct 24, 2026
5a969c1
fix(acp): idle agent errors fail their turns (#24615)
juliusmarminge Oct 25, 2026
0840bb2
fix(provider-acp): Grok monitors show as monitors in background work …
t3dotgg Oct 25, 2026
249b6ae
feat(server): devices on the same network reach background services o…
t3dotgg Oct 25, 2026
7769d70
fix(codex): bound pending requests after app-server stdout closes (#2…
juliusmarminge Oct 25, 2026
ce8d644
fix(cursor): discover built-in skills in the composer (#24595)
juliusmarminge Oct 25, 2026
97548db
fix(server): retry provider event writes after brief SQLite locks (#2…
juliusmarminge Oct 25, 2026
3eb8366
fix(server): settle inactive threads with persisted open PR links (#2…
juliusmarminge Oct 25, 2026
5848bc2
feat(mobile): Settings is a workspace tab, and tabs never open on an …
juliusmarminge Oct 25, 2026
5cf4995
feat(mobile): the folded Duo opens search from a search tab below the…
juliusmarminge Oct 25, 2026
95ffbc9
revert(mobile): take the folded Duo search tab back out for now (#24626)
juliusmarminge Oct 25, 2026
27805ef
fix(web): preview images outside the workspace (#24627)
juliusmarminge Oct 25, 2026
91ea0e1
fix(threads): Working holds only background work the agent waits on (…
t3dotgg Oct 25, 2026
fa1347d
fix(web): switched threads and Scroll to end land on the real end (#2…
t3dotgg Oct 25, 2026
14b828e
fix(web): attach DNG originals as generic files (#24625)
juliusmarminge Oct 25, 2026
cd2b681
fix(server): surface failed runtime request answer delivery (#24616)
juliusmarminge Oct 25, 2026
b8be6da
fix(preview): report navigation timeout accurately (#24356)
Ethereal49 Oct 25, 2026
945fd1d
fix(web): give PR row metadata room below the title (#24553)
juliusmarminge Oct 25, 2026
5ed8b54
fix(mobile): center floating status actions (#24526)
PixPMusic Oct 25, 2026
453bf01
fix(mobile): the iPhone new-chat button sits beside the tab bar on iO…
wojas Oct 25, 2026
7cc3797
Merge remote-tracking branch 'upstream/main' into fork-sync/2026-10-2…
Yarden-zamir Oct 25, 2026
aa6c8ad
fix(mobile): fork settings pages follow Settings as a workspace tab
Yarden-zamir Oct 25, 2026
ba93b8e
docs(server): auto-update comment names awaited monitors
Yarden-zamir Oct 25, 2026
f43cc5e
style(mobile): format pair again
Yarden-zamir Oct 25, 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
5 changes: 5 additions & 0 deletions .macroscope/ignore.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,8 @@ infra/relay/migrations/**/snapshot.json
# Ghostty build output vendored from upstream.
apps/mobile/modules/t3-terminal/Vendor/**
native/libghostty-vt/include/**

# @pierre/highlights compiled to C by wasm2c, plus the wasm2c runtime and simde
# it needs. Regenerated by apps/mobile/scripts/generate-highlights.mts; review
# that script and cpp/t3_highlights.c instead.
apps/mobile/modules/t3-highlights/Vendor/**
1 change: 1 addition & 0 deletions apps/desktop/src/app/DesktopConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const DesktopConfig = Config.all({
otlpProtocol: Config.schema(OtlpProtocol, "T3CODE_OTLP_PROTOCOL").pipe(
Config.withDefault("http/json"),
),
desktopLauncherPath: trimmedString("T3CODE_DESKTOP_LAUNCHER_PATH"),
appImagePath: trimmedString("APPIMAGE"),
disableAutoUpdate: optionalBoolean("T3CODE_DISABLE_AUTO_UPDATE"),
mockUpdates: optionalBoolean("T3CODE_DESKTOP_MOCK_UPDATES"),
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/app/DesktopEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export class DesktopEnvironment extends Context.Service<
readonly linuxDesktopEntryName: string;
readonly linuxWmClass: string;
readonly linuxApplicationsDir: string;
readonly desktopLauncherPath: Option.Option<string>;
readonly appImagePath: Option.Option<string>;
readonly defaultDesktopSettings: DesktopAppSettings.DesktopSettings;
readonly runtimeInfo: DesktopRuntimeInfo;
Expand Down Expand Up @@ -233,6 +234,7 @@ const make = Effect.fn("desktop.environment.make")(function* (
linuxDesktopEntryName: resolveLinuxDesktopEntryName(isDevelopment),
linuxWmClass: isDevelopment ? "t3code-dev" : "t3code",
linuxApplicationsDir,
desktopLauncherPath: config.desktopLauncherPath,
appImagePath: config.appImagePath,
defaultDesktopSettings: DesktopAppSettings.resolveDefaultDesktopSettings(input.appVersion),
runtimeInfo: resolveDesktopRuntimeInfo({
Expand Down
13 changes: 13 additions & 0 deletions apps/desktop/src/app/DesktopLinuxUrlHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const makeEnvironment = (path: Path.Path, overrides: Record<string, unknown> = {
linuxDesktopEntryName: "com.t3tools.T3Code.desktop",
linuxWmClass: "t3code",
linuxApplicationsDir: "/home/alice/.local/share/applications",
desktopLauncherPath: Option.none(),
appImagePath: Option.some("/home/alice/Applications/T3-Code.AppImage"),
path,
...overrides,
Expand Down Expand Up @@ -228,6 +229,18 @@ describe("DesktopLinuxUrlHandler", () => {
});
});

it.effect("launches a distribution package through its configured launcher", () => {
const recorded = emptyRecording();

return Effect.gen(function* () {
yield* runRegister(recorded, {
environment: { desktopLauncherPath: Option.some("/opt/T3 Code/t3code") },
});

assert.include(recorded.files[0]?.content, 'Exec="/opt/T3 Code/t3code" %U');
});
});

it.effect("does not rewrite the pre-ready entry while the portal can be reading it", () => {
const recorded = emptyRecording();

Expand Down
7 changes: 6 additions & 1 deletion apps/desktop/src/app/DesktopLinuxUrlHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,14 @@ export const make = Effect.gen(function* () {
const themeIconDir = environment.path.join(hicolorDir, LINUX_THEME_ICON_SIZE, "apps");

const writeDesktopEntry = Effect.gen(function* () {
// Distribution packages that run on a shared Electron set
// T3CODE_DESKTOP_LAUNCHER_PATH, because process.execPath is that Electron.
// Inside the mounted AppImage, process.execPath points at a transient
// /tmp/.mount_* path — the handler must launch the AppImage itself.
const execTarget = Option.getOrElse(environment.appImagePath, () => process.execPath);
const execTarget = environment.desktopLauncherPath.pipe(
Option.orElse(() => environment.appImagePath),
Option.getOrElse(() => process.execPath),
);
const content = renderUrlHandlerDesktopEntry({
displayName: environment.displayName,
execTarget,
Expand Down
22 changes: 22 additions & 0 deletions apps/desktop/src/app/DesktopPreReadyPlatform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,28 @@ describe("DesktopPreReadyPlatform", () => {
).pipe(Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())));
});

it.effect("points the early portal entry at a configured distribution launcher", () => {
vi.stubEnv("VITE_DEV_SERVER_URL", "");
vi.stubEnv("XDG_DATA_HOME", "/xdg");
vi.stubEnv("APPIMAGE", "/Applications/current.AppImage");
vi.stubEnv("T3CODE_DESKTOP_LAUNCHER_PATH", " /opt/T3 Code/t3code ");
getSwitchValueMock.mockReturnValue("");

return Effect.scoped(
Effect.gen(function* () {
yield* Layer.build(
DesktopPreReadyPlatform.layer.pipe(
Layer.provide(Layer.succeed(HostProcess.Platform, "linux")),
),
);
const entry = writeFileSyncMock.mock.calls.find(
([path]) => path === "/xdg/applications/com.t3tools.T3Code.desktop",
)?.[1];
assert.include(entry, 'Exec="/opt/T3 Code/t3code" %U');
}),
).pipe(Effect.ensuring(Effect.sync(() => vi.unstubAllEnvs())));
});

it.effect(
"acquires a synchronous pre-ready layer before an asynchronous Clerk-shaped layer",
() =>
Expand Down
5 changes: 4 additions & 1 deletion apps/desktop/src/app/DesktopPreReadyPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ export const make = Effect.gen(function* () {
isDevelopment: linux.isDevelopment,
appVersion: Electron.app.getVersion(),
}).displayName,
execTarget: process.env.APPIMAGE?.trim() || process.execPath,
execTarget:
process.env.T3CODE_DESKTOP_LAUNCHER_PATH?.trim() ||
process.env.APPIMAGE?.trim() ||
process.execPath,
scheme: ElectronProtocol.getDesktopScheme(linux.isDevelopment),
...(iconPath === undefined ? {} : { iconPath }),
}),
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/.swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
included:
- ios/T3Code
- modules/t3-composer-editor/ios
- modules/t3-highlights/ios
- modules/t3-native-controls/ios
- modules/t3-terminal/ios
- modules/t3-review-diff/ios
Expand Down
8 changes: 0 additions & 8 deletions apps/mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,6 @@ Build and run the local iOS preview app:
vp run ios:preview
```

Force the review diff highlighter engine:

```bash
EXPO_PUBLIC_REVIEW_HIGHLIGHTER_ENGINE=javascript vp run ios:dev
```

`javascript` is the default and recommended setting for the review diff screen. Set `EXPO_PUBLIC_REVIEW_HIGHLIGHTER_ENGINE=native` only when you explicitly want to test the native Shiki engine.

Inspect the resolved Expo config for a variant:

```bash
Expand Down
1 change: 0 additions & 1 deletion apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,6 @@ const config: ExpoConfig = {
...(!isIosPersonalTeamBuild ? ["./plugins/withWidgetLogoAsset.cjs", widgetsPlugin] : []),
"./plugins/withAndroidCleartextTraffic.cjs",
"./plugins/withAndroidGradleHeap.cjs",
"./plugins/withAndroidFbjniVersion.cjs",
"./plugins/withAndroidInputBackground.cjs",
"./plugins/withAndroidModernPopupMenu.cjs",
"./plugins/withAndroidModernAlertDialog.cjs",
Expand Down
23 changes: 0 additions & 23 deletions apps/mobile/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,8 @@ const licenseGeneratorSource = path.join(
"third-party-licenses.ts",
);
const escapedWorkspaceRoot = workspaceRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const mobileShikiRoot = path.dirname(require.resolve("shiki/package.json", { paths: [__dirname] }));
const generatedDeviceStreamRoot = path.join(__dirname, ".generated", "device-stream");
const generatedPreviewStreamRoot = path.join(__dirname, ".generated", "preview-stream");
const resolveShikiDependencyRoot = (packageName) => {
const entryPath = require.resolve(packageName, { paths: [mobileShikiRoot] });
let currentDir = path.dirname(entryPath);

while (!fs.existsSync(path.join(currentDir, "package.json"))) {
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) {
throw new Error(`Could not resolve package root for ${packageName}`);
}
currentDir = parentDir;
}

return currentDir;
};

config.watchFolders = [...new Set([...(config.watchFolders ?? []), workspaceRoot])];
config.resolver = {
Expand All @@ -50,14 +35,6 @@ config.resolver = {
"@t3tools/mobile-third-party-licenses": generatedLicenseModuleRoot,
"@t3tools/mobile-device-stream": generatedDeviceStreamRoot,
"@t3tools/mobile-preview-stream": generatedPreviewStreamRoot,
shiki: mobileShikiRoot,
"@shikijs/core": resolveShikiDependencyRoot("@shikijs/core"),
"@shikijs/engine-javascript": resolveShikiDependencyRoot("@shikijs/engine-javascript"),
"@shikijs/engine-oniguruma": resolveShikiDependencyRoot("@shikijs/engine-oniguruma"),
"@shikijs/langs": resolveShikiDependencyRoot("@shikijs/langs"),
"@shikijs/themes": resolveShikiDependencyRoot("@shikijs/themes"),
"@shikijs/types": resolveShikiDependencyRoot("@shikijs/types"),
"@shikijs/vscode-textmate": resolveShikiDependencyRoot("@shikijs/vscode-textmate"),
},
};

Expand Down
30 changes: 30 additions & 0 deletions apps/mobile/modules/t3-highlights/T3HighlightsNative.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'json'

package = JSON.parse(File.read(File.join(__dir__, 'package.json')))

Pod::Spec.new do |s|
s.name = 'T3HighlightsNative'
s.version = package['version']
s.summary = 'Native @pierre/highlights syntax highlighter for T3 Code mobile.'
s.description = 'Runs the @pierre/highlights lexers, compiled from WebAssembly to C with wasm2c.'
s.homepage = 'https://t3tools.com'
s.license = { :type => 'UNLICENSED' }
s.author = { 'T3 Tools' => 'hello@t3tools.com' }
s.platforms = { :ios => '16.1' }
s.source = { :path => '.' }
s.source_files = 'ios/**/*.swift', 'cpp/**/*.{c,h,inc}', 'Vendor/**/*.{c,h,inc}'
# Swift sees only the wrapper; the generated and runtime headers stay private.
s.public_header_files = 'cpp/t3_highlights.h'
# Debug builds compile at -O0 by default, which makes the lexers several times
# slower. They are generated code, so optimize them in every configuration.
s.compiler_flags = '-O2'
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
# Plain malloc memory with explicit bounds checks: no guard-page signal
# handlers competing with the app's crash reporting.
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) WASM_RT_USE_MMAP=0 WASM_RT_MEMCHECK_BOUNDS_CHECK=1 WASM_RT_SKIP_SIGNAL_RECOVERY=1',
'HEADER_SEARCH_PATHS' => '$(inherited) "${PODS_TARGET_SRCROOT}/Vendor" "${PODS_TARGET_SRCROOT}/Vendor/highlights" "${PODS_TARGET_SRCROOT}/Vendor/wasm2c"',
}
s.swift_version = '5.9'
s.dependency 'ExpoModulesCore'
end
Loading
Loading