From 86b03c6c3e7a5bffe4e1831dd77302287b295186 Mon Sep 17 00:00:00 2001 From: Horus Lugo Date: Sun, 28 Jun 2026 17:53:59 +0200 Subject: [PATCH 1/5] fix(mobile): build Android preview release APK --- apps/mobile/app.config.ts | 19 +-- .../withAndroidWidgetStringResources.cjs | 64 ++++++++++ package.json | 1 + .../build-mobile-android-preview-release.ts | 117 ++++++++++++++++++ 4 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 apps/mobile/plugins/withAndroidWidgetStringResources.cjs create mode 100644 scripts/build-mobile-android-preview-release.ts diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index 8cdf6f2e25c..42e9358be0c 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -55,6 +55,15 @@ function resolveAppVariant(value: string | undefined): AppVariant { const variant = VARIANT_CONFIG[APP_VARIANT]; +const WIDGETS = [ + { + name: "AgentActivity", + displayName: "Agent Activity", + description: "Shows the current state of active T3 Code agents.", + supportedFamilies: ["systemSmall", "systemMedium", "accessoryRectangular"], + }, +]; + const config: ExpoConfig = { name: variant.appName, slug: "t3-code", @@ -145,16 +154,10 @@ const config: ExpoConfig = { bundleIdentifier: `${variant.iosBundleIdentifier}.widgets`, groupIdentifier: `group.${variant.iosBundleIdentifier}`, enablePushNotifications: true, - widgets: [ - { - name: "AgentActivity", - displayName: "Agent Activity", - description: "Shows the current state of active T3 Code agents.", - supportedFamilies: ["systemSmall", "systemMedium", "accessoryRectangular"], - }, - ], + widgets: WIDGETS, }, ], + ["./plugins/withAndroidWidgetStringResources.cjs", { widgets: WIDGETS }], "./plugins/withAndroidCleartextTraffic.cjs", ], extra: { diff --git a/apps/mobile/plugins/withAndroidWidgetStringResources.cjs b/apps/mobile/plugins/withAndroidWidgetStringResources.cjs new file mode 100644 index 00000000000..16fcc96835a --- /dev/null +++ b/apps/mobile/plugins/withAndroidWidgetStringResources.cjs @@ -0,0 +1,64 @@ +const fs = require("node:fs"); +const path = require("node:path"); + +const { withDangerousMod } = require("expo/config-plugins"); + +const toAndroidResourceName = (name) => { + const resourceName = name + .replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2") + .replace(/([a-z0-9])([A-Z])/g, "$1_$2") + .replace(/[^A-Za-z0-9_]/g, "_") + .replace(/_+/g, "_") + .replace(/^_+|_+$/g, "") + .toLowerCase(); + + return /^[a-z]/.test(resourceName) ? resourceName : `widget_${resourceName}`; +}; + +const escapeXmlSpecialChars = (value) => + value + .replace(/&/g, "&") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(//g, ">"); + +const createWidgetStringsXml = (widgets) => ` + +${widgets + .map((widget) => { + const resourceName = toAndroidResourceName(widget.name); + + return ` ${escapeXmlSpecialChars( + widget.displayName, + )} + ${escapeXmlSpecialChars(widget.description)}`; + }) + .join("\n")} + +`; + +module.exports = function withAndroidWidgetStringResources(config, props = {}) { + return withDangerousMod(config, [ + "android", + (nextConfig) => { + const widgets = Array.isArray(props.widgets) ? props.widgets : []; + if (widgets.length === 0) { + return nextConfig; + } + + const valuesDirectory = path.join( + nextConfig.modRequest.platformProjectRoot, + "app/src/main/res/values", + ); + + fs.mkdirSync(valuesDirectory, { recursive: true }); + fs.writeFileSync( + path.join(valuesDirectory, "expo_widgets.xml"), + createWidgetStringsXml(widgets), + ); + + return nextConfig; + }, + ]); +}; diff --git a/package.json b/package.json index f97275e60bb..5f8a384d484 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "dist:desktop:win": "node scripts/build-desktop-artifact.ts --platform win --target nsis", "dist:desktop:win:arm64": "node scripts/build-desktop-artifact.ts --platform win --target nsis --arch arm64", "dist:desktop:win:x64": "node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64", + "dist:mobile:android:preview:release": "node scripts/build-mobile-android-preview-release.ts", "release:smoke": "node scripts/release-smoke.ts", "clean": "rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/dist apps/*/dist-electron packages/*/dist .vite-plus apps/*/.vite-plus packages/*/.vite-plus", "sync:repos": "node scripts/sync-reference-repos.ts" diff --git a/scripts/build-mobile-android-preview-release.ts b/scripts/build-mobile-android-preview-release.ts new file mode 100644 index 00000000000..a3b52c95e69 --- /dev/null +++ b/scripts/build-mobile-android-preview-release.ts @@ -0,0 +1,117 @@ +#!/usr/bin/env node +// @effect-diagnostics nodeBuiltinImport:off + +import * as NodeChildProcess from "node:child_process"; +import * as NodeCrypto from "node:crypto"; +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; + +const repoRoot = NodePath.resolve(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)), ".."); +const mobileRoot = NodePath.join(repoRoot, "apps/mobile"); +const androidRoot = NodePath.join(mobileRoot, "android"); +const releaseApkPath = NodePath.join(androidRoot, "app/build/outputs/apk/release/app-release.apk"); +const destinationPath = resolveDestinationPath(); +const releaseArchitectures = process.env.T3CODE_ANDROID_PREVIEW_RELEASE_ARCHITECTURES?.trim(); + +function resolveDestinationPath(): string { + const explicitPath = process.env.T3CODE_ANDROID_PREVIEW_RELEASE_APK_PATH?.trim(); + return explicitPath + ? NodePath.resolve(repoRoot, explicitPath) + : NodePath.join(repoRoot, "dist/mobile/android/t3code-preview-release.apk"); +} + +function log(message: string): void { + process.stdout.write(`${message}\n`); +} + +function runCommand( + command: string, + args: ReadonlyArray, + options: { + readonly cwd: string; + readonly env?: NodeJS.ProcessEnv; + }, +): void { + log(`$ ${[command, ...args].join(" ")}`); + const result = NodeChildProcess.spawnSync(command, args, { + cwd: options.cwd, + env: { + ...process.env, + ...options.env, + }, + shell: false, + stdio: "inherit", + }); + + if (result.error) { + throw result.error; + } + + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +function copyApk(): void { + if (!NodeFS.existsSync(releaseApkPath)) { + throw new Error(`Expected release APK was not created: ${releaseApkPath}`); + } + + NodeFS.mkdirSync(NodePath.dirname(destinationPath), { recursive: true }); + NodeFS.copyFileSync(releaseApkPath, destinationPath); + + const size = NodeFS.statSync(destinationPath).size; + const hash = NodeCrypto.createHash("sha256") + .update(NodeFS.readFileSync(destinationPath)) + .digest("hex"); + + log(`Copied APK to ${destinationPath}`); + log(`Size: ${(size / 1024 / 1024).toFixed(1)} MiB`); + log(`SHA-256: ${hash}`); +} + +runCommand( + "vp", + [ + "exec", + "--filter", + "@t3tools/mobile", + "--", + "expo", + "prebuild", + "--clean", + "--no-install", + "--platform", + "android", + ], + { + cwd: repoRoot, + env: { + APP_VARIANT: "preview", + EXPO_NO_GIT_STATUS: "1", + NODE_ENV: "production", + }, + }, +); + +runCommand( + "./gradlew", + [ + "--no-daemon", + "--stacktrace", + "--build-cache", + "-Dorg.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m", + ...(releaseArchitectures ? [`-PreactNativeArchitectures=${releaseArchitectures}`] : []), + "assembleRelease", + ], + { + cwd: androidRoot, + env: { + APP_VARIANT: "preview", + NODE_ENV: "production", + }, + }, +); + +copyApk(); From 5e19dcf6a47df0607a244e9943da407895f4bb3c Mon Sep 17 00:00:00 2001 From: Horus Lugo Date: Sun, 28 Jun 2026 17:54:04 +0200 Subject: [PATCH 2/5] fix(mobile): respect safe area on Android home --- apps/mobile/src/features/home/HomeScreen.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/mobile/src/features/home/HomeScreen.tsx b/apps/mobile/src/features/home/HomeScreen.tsx index 7ee5660edf1..3d12b49d3d2 100644 --- a/apps/mobile/src/features/home/HomeScreen.tsx +++ b/apps/mobile/src/features/home/HomeScreen.tsx @@ -8,6 +8,7 @@ import type { SidebarThreadSortOrder, } from "@t3tools/contracts"; import * as Haptics from "expo-haptics"; +import { useHeaderHeight } from "expo-router/build/react-navigation/elements"; import { SymbolView } from "expo-symbols"; import { useCallback, useMemo, useRef, useState } from "react"; import { ActivityIndicator, Pressable, ScrollView, useWindowDimensions, View } from "react-native"; @@ -440,6 +441,9 @@ export function HomeScreen(props: HomeScreenProps) { const [expandedProjects, setExpandedProjects] = useState>(() => new Set()); const openSwipeableRef = useRef(null); const insets = useSafeAreaInsets(); + const headerHeight = useHeaderHeight(); + const contentTopInset = Math.max(headerHeight, insets.top); + const contentBottomInset = Math.max(insets.bottom, 12); const accentColor = useThemeColor("--color-icon-muted"); const toggleExpanded = useCallback((key: string) => { @@ -507,7 +511,9 @@ export function HomeScreen(props: HomeScreenProps) { return ( From 8cc4b001060ada76c9c237f88161a4674530ea96 Mon Sep 17 00:00:00 2001 From: Horus Lugo Date: Sun, 28 Jun 2026 17:54:11 +0200 Subject: [PATCH 3/5] ci: publish Android APK previews from PR label --- .github/workflows/issue-labels.yml | 5 + .../workflows/mobile-android-apk-preview.yml | 151 ++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 .github/workflows/mobile-android-apk-preview.yml diff --git a/.github/workflows/issue-labels.yml b/.github/workflows/issue-labels.yml index d6571d65d45..607d6c26cd7 100644 --- a/.github/workflows/issue-labels.yml +++ b/.github/workflows/issue-labels.yml @@ -37,6 +37,11 @@ jobs: color: "fbca04", description: "Issue needs maintainer review and initial categorization.", }, + { + name: "android:apk-preview", + color: "0e8a16", + description: "Build and publish a self-contained Android preview APK for this PR.", + }, ]; for (const label of managedLabels) { diff --git a/.github/workflows/mobile-android-apk-preview.yml b/.github/workflows/mobile-android-apk-preview.yml new file mode 100644 index 00000000000..7cccb086806 --- /dev/null +++ b/.github/workflows/mobile-android-apk-preview.yml @@ -0,0 +1,151 @@ +name: Mobile Android APK Preview + +on: + pull_request: + types: [labeled] + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + build: + name: Build Android APK preview + if: github.event.label.name == 'android:apk-preview' + runs-on: ubuntu-24.04 + timeout-minutes: 90 + concurrency: + group: android-apk-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + env: + ANDROID_APK_PATH: dist/mobile/android/t3code-preview-release.apk + APP_VARIANT: preview + NODE_OPTIONS: --max-old-space-size=8192 + T3CODE_ANDROID_PREVIEW_RELEASE_APK_PATH: dist/mobile/android/t3code-preview-release.apk + T3CODE_ANDROID_PREVIEW_RELEASE_ARCHITECTURES: arm64-v8a + steps: + - name: Checkout PR head + uses: actions/checkout@v6 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Setup Vite+ + uses: voidzero-dev/setup-vp@v1 + with: + node-version-file: package.json + cache: true + run-install: true + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Build Android preview release APK + run: vp run dist:mobile:android:preview:release + + - id: apk + name: Verify APK artifact + shell: bash + run: | + set -euo pipefail + + test -f "$ANDROID_APK_PATH" + unzip -l "$ANDROID_APK_PATH" | grep -E 'assets/(index\.android\.bundle|app\.manifest)' + + size_bytes="$(stat -c '%s' "$ANDROID_APK_PATH")" + size_mib="$(awk "BEGIN { printf \"%.1f\", $size_bytes / 1024 / 1024 }")" + sha256="$(sha256sum "$ANDROID_APK_PATH" | awk '{ print $1 }')" + + echo "size_mib=$size_mib" >> "$GITHUB_OUTPUT" + echo "sha256=$sha256" >> "$GITHUB_OUTPUT" + + - id: upload-apk + name: Upload APK artifact + uses: actions/upload-artifact@v7 + with: + name: t3code-preview-release-pr-${{ github.event.pull_request.number }} + path: ${{ env.ANDROID_APK_PATH }} + if-no-files-found: error + retention-days: 14 + + - name: Comment APK artifact and remove trigger label + uses: actions/github-script@v8 + env: + APK_ARTIFACT_NAME: t3code-preview-release-pr-${{ github.event.pull_request.number }} + APK_ARTIFACT_URL: ${{ steps.upload-apk.outputs.artifact-url }} + APK_SHA256: ${{ steps.apk.outputs.sha256 }} + APK_SIZE_MIB: ${{ steps.apk.outputs.size_mib }} + APK_SOURCE_SHA: ${{ github.event.pull_request.head.sha }} + LABEL_NAME: android:apk-preview + with: + script: | + const marker = ""; + const issueNumber = context.payload.pull_request.number; + const serverUrl = process.env.GITHUB_SERVER_URL ?? "https://github.com"; + const artifactUrl = process.env.APK_ARTIFACT_URL || `${serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const shortSha = (process.env.APK_SOURCE_SHA ?? "").slice(0, 12); + const body = [ + marker, + "## Android APK preview", + "", + "A self-contained Android preview APK was built for this PR.", + "", + `- Artifact: [${process.env.APK_ARTIFACT_NAME}](${artifactUrl})`, + `- Size: ${process.env.APK_SIZE_MIB} MiB`, + `- SHA-256: \`${process.env.APK_SHA256}\``, + `- Source: \`${shortSha}\``, + "- Note: GitHub Actions downloads artifacts as zip archives.", + "", + "Re-add the `android:apk-preview` label to build a fresh APK.", + ].join("\n"); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + per_page: 100, + }); + + const existing = comments.find((comment) => + comment.user?.type === "Bot" && comment.body?.includes(marker), + ); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body, + }); + } + + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + name: process.env.LABEL_NAME, + }); + } catch (error) { + if (error.status !== 404) { + throw error; + } + } From bdc3514153ac688a45f68e3e8270b7a51d5d8a9f Mon Sep 17 00:00:00 2001 From: Horus Lugo Date: Sun, 28 Jun 2026 19:51:43 +0200 Subject: [PATCH 4/5] chore: refresh lockfile after Android merge --- pnpm-lock.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1a430156be..9b88fdc75cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14448,19 +14448,19 @@ snapshots: '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.1.3) babel-plugin-react-compiler: 1.0.0 - '@vitest/browser-preview@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9)': + '@vitest/browser-preview@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)))': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9) - vitest: 4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) + '@vitest/browser': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))) + vitest: 4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9))(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9)': + '@vitest/browser@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)))': dependencies: '@blazediff/core': 1.9.1 '@vitest/mocker': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) @@ -14469,7 +14469,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) + vitest: 4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9))(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil @@ -20129,8 +20129,8 @@ snapshots: dependencies: '@oxc-project/types': 0.136.0 '@oxlint/plugins': 1.68.0 - '@vitest/browser': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9) - '@vitest/browser-preview': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9) + '@vitest/browser': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))) + '@vitest/browser-preview': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))) '@vitest/expect': 4.1.9 '@vitest/mocker': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) '@vitest/pretty-format': 4.1.9 @@ -20143,7 +20143,7 @@ snapshots: oxlint: 1.70.0(oxlint-tsgolint@0.23.0)(vite-plus@0.2.1(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)) oxlint-tsgolint: 0.23.0 vite: '@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' - vitest: 4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) + vitest: 4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9))(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) optionalDependencies: '@voidzero-dev/vite-plus-darwin-arm64': 0.2.1 '@voidzero-dev/vite-plus-darwin-x64': 0.2.1 @@ -20189,7 +20189,7 @@ snapshots: optionalDependencies: vite: '@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' - vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)): + vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9))(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)): dependencies: '@vitest/expect': 4.1.9 '@vitest/mocker': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3)) @@ -20213,7 +20213,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.12.4 - '@vitest/browser-preview': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9) + '@vitest/browser-preview': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))(utf-8-validate@6.0.6)(vitest@4.1.9(@types/node@24.12.4)(@vitest/browser-preview@4.1.9)(@voidzero-dev/vite-plus-core@0.2.1(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@6.0.3))) transitivePeerDependencies: - msw From 1d80d97314f40f793c6859a2ad59ed79bfc4176b Mon Sep 17 00:00:00 2001 From: Horus Lugo Date: Sun, 28 Jun 2026 21:21:40 +0200 Subject: [PATCH 5/5] fix(mobile): align Android terminal for 16KB pages --- .../t3-terminal/android/src/main/cpp/CMakeLists.txt | 4 ++++ .../0002-align-android-libvt-to-16kb-pages.patch | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 apps/mobile/modules/t3-terminal/scripts/libghostty-android-patches/0002-align-android-libvt-to-16kb-pages.patch diff --git a/apps/mobile/modules/t3-terminal/android/src/main/cpp/CMakeLists.txt b/apps/mobile/modules/t3-terminal/android/src/main/cpp/CMakeLists.txt index 89f7d8c5e5d..0273ff6c864 100644 --- a/apps/mobile/modules/t3-terminal/android/src/main/cpp/CMakeLists.txt +++ b/apps/mobile/modules/t3-terminal/android/src/main/cpp/CMakeLists.txt @@ -16,4 +16,8 @@ target_include_directories( PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../../../Vendor/libghostty-vt/include" ) +target_link_options( + t3terminal PRIVATE "-Wl,-z,common-page-size=16384" "-Wl,-z,max-page-size=16384" +) + target_link_libraries(t3terminal PRIVATE ghostty-vt log) diff --git a/apps/mobile/modules/t3-terminal/scripts/libghostty-android-patches/0002-align-android-libvt-to-16kb-pages.patch b/apps/mobile/modules/t3-terminal/scripts/libghostty-android-patches/0002-align-android-libvt-to-16kb-pages.patch new file mode 100644 index 00000000000..2fdffd2ccb4 --- /dev/null +++ b/apps/mobile/modules/t3-terminal/scripts/libghostty-android-patches/0002-align-android-libvt-to-16kb-pages.patch @@ -0,0 +1,11 @@ +diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig +--- a/src/build/GhosttyLibVt.zig ++++ b/src/build/GhosttyLibVt.zig +@@ -236,6 +236,7 @@ fn initLib( + if (lib.rootModuleTarget().abi.isAndroid()) { + // Support 16kb page sizes, required for Android 15+. ++ lib.link_z_common_page_size = 16384; // 16kb + lib.link_z_max_page_size = 16384; // 16kb + + try @import("android_ndk").addPaths(b, lib); + }