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
72ebd98
fix(web): show pointer cursors on changed-file controls (#22266)
UtkarshUsername Oct 20, 2026
b5a0696
fix(web): use the shared markdown scrollbar width (#22200)
UtkarshUsername Oct 20, 2026
573d376
revert: limit bars with many accounts no longer clip names to one let…
juliusmarminge Oct 20, 2026
8ecb607
fix(mobile): pin expo-modules-core so the release smoke patch stays i…
Project516 Oct 20, 2026
2b28b46
fix(server): updates no longer keep every old runtime on disk (#22253)
juliusmarminge Oct 20, 2026
cdaaa74
fix(mobile): chat chips match desktop and the composer again (#22169)
flamboh Oct 20, 2026
2229efc
fix(web): normalize disabled terminal button opacity (#22389)
UtkarshUsername Oct 20, 2026
d652490
fix(contracts): settings save again when the writing style has defaul…
MatiasGadeberg Oct 20, 2026
758d773
fix(relay): run the relay Worker next to its database (#22065)
juliusmarminge Oct 20, 2026
81dea78
test(web): disposable render helper and lint rule for browser tests (…
juliusmarminge Oct 20, 2026
be5a5cd
fix(mobile): use native sheet headers and toolbar filters (#22390)
juliusmarminge Oct 20, 2026
2893437
test(ssh): unreaped-server test no longer loses its zombie fixture (#…
juliusmarminge Oct 20, 2026
6ab39ff
test(web): require-disposable-render is now an error (#22410)
juliusmarminge Oct 20, 2026
f7f9e21
test(web): migrate the last .ts browser tests and stop wrapping userE…
juliusmarminge Oct 20, 2026
5d4c202
fix(web,mobile): Limits bars show the even-pace mark as a tick in a g…
juliusmarminge Oct 20, 2026
7e289ae
test(web): pooled Limits segments narrower than 6px draw no pace mark…
juliusmarminge Oct 20, 2026
dac3437
fix(mobile): the composer stays one row until the draft wraps (#20812)
juliusmarminge Oct 20, 2026
860e6d2
fix(web): restore left-aligned markdown table cells (#22482)
t3-code[bot] Oct 20, 2026
0c85b0d
feat(scheduled-tasks): event filters for webhook deliveries (#22194)
juliusmarminge Oct 20, 2026
ec58572
fix(mobile): prevent update checker from crashing iOS at launch (#22488)
juliusmarminge Oct 20, 2026
2ebe280
feat(scheduled-tasks): replay dedupe and coalescing for webhook deliv…
juliusmarminge Oct 20, 2026
14b47ee
ci(release): Make Windows arm64 CI job 50% faster (#22428)
jahands Oct 20, 2026
4c48118
ci(release): Windows arm64 verify no longer runs the crashing ARM Pow…
t3dotgg Oct 20, 2026
ca140c4
refactor(source-control): GitCafe creates repositories and pull reque…
juliusmarminge Oct 20, 2026
5bcc90e
test(web): fix release browser test failures (#22508)
t3dotgg Oct 20, 2026
c8667fd
fix(mobile): connecting an environment no longer crashes the header (…
juliusmarminge Oct 20, 2026
4af3231
fix(mobile): folded Back clears the detail route before the next sele…
juliusmarminge Oct 20, 2026
fe27e8e
ci(release): release tests stop timing out and finish in about 5 minu…
t3dotgg Oct 20, 2026
5d27ecd
test(server): Claude steer test no longer hangs for 120s under CI loa…
t3dotgg Oct 20, 2026
f9dd259
test: remove the timing races behind recurring CI and release failure…
t3dotgg Oct 20, 2026
fcb20e4
feat(web): render Mermaid diagrams with pele (#21472)
maria-rcks Oct 20, 2026
805039f
Merge upstream fcb20e4 into patch/release
fireboltdude1357 Oct 21, 2026
ac93032
test(web): drop the Mermaid warmup that imports a removed package
fireboltdude1357 Oct 21, 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/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ jobs:

# The web suite is the largest outside the server. In the shared Test job it
# started last and ran alone for about 40s after everything else finished.
# release.yml mirrors this test, test_web, and test_server split.
test_web:
name: Test Web
needs: changes
Expand Down
420 changes: 272 additions & 148 deletions .github/workflows/release-desktop.yml

Large diffs are not rendered by default.

64 changes: 56 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ jobs:
run: vp run typecheck

# Keep tests on their own runners, using the same package split and server
# shards as CI, so the release check job does not spend its budget on tests.
# shards as CI (test, test_web, test_server in ci.yml). Keep the two in sync.
test:
name: Release tests
needs: [preflight]
Expand Down Expand Up @@ -286,23 +286,67 @@ jobs:
sudo sed -i 's|http://|https://|g' /etc/apt/blacksmith-ubuntu-mirrors.txt /etc/apt/sources.list.d/ubuntu.sources
sudo apt-get update && sudo apt-get install -y libsecret-1-dev pkg-config build-essential

# The web package's browser project runs React tests in headless Chromium.
- name: Test
run: vp run --parallel --concurrency-limit 4 --filter '!t3' --filter '!@t3tools/monorepo' --filter '!@t3tools/web' test

# Web browser tests time out when they share a runner with the other package
# suites: Chromium gets too little CPU and diff workers miss their 10s start budget.
test_web:
name: Release web tests
needs: [preflight]
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' }}
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.ref }}
sparse-checkout: |
/*
!/.repos/
sparse-checkout-cone-mode: false

- name: Setup dependencies
uses: ./.github/actions/setup-ci-deps

- name: Resolve Playwright version
id: playwright_version
run: echo "version=$(node -p "require('./apps/web/node_modules/playwright/package.json').version")" >> "$GITHUB_OUTPUT"

- name: Cache Chromium
id: chromium_cache
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-chromium-shell-${{ runner.os }}-${{ runner.arch }}-${{ steps.playwright_version.outputs.version }}

- name: Install Chromium for web browser tests
run: node apps/web/node_modules/playwright/cli.js install --with-deps --only-shell chromium
if: steps.chromium_cache.outputs.cache-hit != 'true'
run: node apps/web/node_modules/playwright/cli.js install --only-shell chromium

- name: Install missing Chromium system dependencies
run: |
browser=$(find "$HOME/.cache/ms-playwright" -name chrome-headless-shell -type f -print -quit)
test -n "$browser"
if ldd "$browser" | grep '=> not found'; then
node apps/web/node_modules/playwright/cli.js install-deps chromium
fi

- name: Test
run: vp run --parallel --concurrency-limit 4 --filter '!t3' --filter '!@t3tools/monorepo' test
run: vp run --filter @t3tools/web test

# Server test files run one at a time, so more small runners beat fewer big ones.
test_server:
name: Release server tests ${{ matrix.shard }}
needs: [preflight]
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' }}
runs-on: blacksmith-8vcpu-ubuntu-2404
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- name: Checkout
uses: actions/checkout@v6
Expand Down Expand Up @@ -659,6 +703,8 @@ jobs:
resource_key: win32-x64
cli_archive: true

# Windows arm64 runners have 4 vCPUs, and the packaging is mostly CPU-bound,
# so the build runs on Linux and only the native checks run on arm64.
desktop_win_arm64:
name: Desktop Windows arm64
needs: [preflight, relay_public_config, build_bundle]
Expand All @@ -674,7 +720,8 @@ jobs:
clerk_cli_oauth_client_id: ${{ needs.relay_public_config.outputs.clerk_cli_oauth_client_id }}
relay_url: ${{ needs.relay_public_config.outputs.relay_url }}
label: Windows arm64
runner: windows-11-arm
runner: blacksmith-16vcpu-ubuntu-2404
verify_runner: windows-11-arm
platform: win
target: nsis
arch: arm64
Expand All @@ -690,6 +737,7 @@ jobs:
relay_public_config,
quality,
test,
test_web,
test_server,
desktop_mac_arm64,
desktop_mac_x64,
Expand All @@ -698,7 +746,7 @@ jobs:
desktop_win_x64,
desktop_win_arm64,
]
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.test.result == 'success' && needs.test_server.result == 'success' && needs.desktop_mac_arm64.result == 'success' && needs.desktop_mac_x64.result == 'success' && needs.desktop_linux_x64.result == 'success' && needs.desktop_linux_arm64.result == 'success' && needs.desktop_win_x64.result == 'success' && needs.desktop_win_arm64.result == 'success' }}
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.test.result == 'success' && needs.test_web.result == 'success' && needs.test_server.result == 'success' && needs.desktop_mac_arm64.result == 'success' && needs.desktop_mac_x64.result == 'success' && needs.desktop_linux_x64.result == 'success' && needs.desktop_linux_arm64.result == 'success' && needs.desktop_win_x64.result == 'success' && needs.desktop_win_arm64.result == 'success' }}
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 30
permissions:
Expand Down
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ An empty database is a bad test. Seed your worktree's `.t3` with a copy of real
- **Do not run repo-wide checks.** No `vp check`, no `vp run -r test`, no `vp run -r typecheck` unless I ask. CI owns the full suite.
- Backend behavior changes ship with focused tests for that behavior.
- The server is event-sourced, and side effects run after the command commits. In tests, drain the effect worker (`OrchestrationEffectWorkerV2.drain`) or await the specific persisted event or `Deferred` that marks the milestone. Never wait on sleeps or polling. A test that needs a timeout to pass is wrong.
- Web browser tests are `*.browser.test.tsx` or `*.browser.test.ts` and render with `await using screen = await render(...)` from `apps/web/test/render.tsx`. `t3code/require-disposable-render` errors on the old patterns.
- Acquire what the test drives after mounting (fake timers it advances, spies it sets up after mounting) after the render, so it disposes before the unmount. Anything the first render reads comes before it.
- A helper that renders and then waits or clicks puts the screen in an `AsyncDisposableStack` and `move()`s it out only on success, so a failed wait still unmounts.
- `rerender` needs a fresh element on every call.
- No lifecycle hooks and no module-level mutable state. Shared mock state that `vi.mock` factories read is reset by a `setup()` call at the top of the test.
- `router` is on by default. Pass `router: false` only with a one-line comment saying why.

```tsx
it("saves on Enter", async () => {
const onSave = vi.fn();
using _now = vi.spyOn(Date, "now").mockReturnValue(0);
await using screen = await render(<TitleEditor onSave={onSave} />, { router: "/threads/1" });
await userEvent.type(screen.getByRole("textbox"), "Title{Enter}");
expect(onSave).toHaveBeenCalledWith("Title");
});
```

- Upon request, user-visible frontend changes should get one integrated pass in a real client: `test-t3-app` for web, `test-t3-mobile` for mobile. The primary agent does this once after integrating. Subagents do not launch their own dev servers. Ask permission before doing computer use or spinning up browsers.

For authorized mobile verification, a missing or outdated native client is a build step, not a blocker. Run `node scripts/mobile-native-client.ts ensure <ios|android> <device-id>` on the simulator host before starting Metro. It checks the local Expo fingerprint and builds/installs when needed. See `test-t3-mobile` for the full workflow.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/assets/context-chip-icons/film.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/assets/context-chip-icons/git-merge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/assets/context-chip-icons/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/assets/context-chip-icons/skill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/mobile/assets/context-chip-icons/terminal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class T3ComposerEditorView(context: Context, appContext: AppContext) : ExpoView(
private var applyingNativeValue = false
private var desiredLineHeightPx = 0
private var lastContentHeight = 0
private var lastContentWidth = 0
private var contentInsetVertical = 0
private var tokensJson = "[]"
private var tokens: List<ComposerToken> = emptyList()
Expand Down Expand Up @@ -424,12 +425,19 @@ class T3ComposerEditorView(context: Context, appContext: AppContext) : ExpoView(
}

private fun emitContentSizeIfNeeded() {
val height = editor.layout?.height ?: editor.measuredHeight
val contentHeight = height + contentInsetVertical * 2
if (contentHeight == lastContentHeight) return
// The laid-out text without insets. A one-line draft reports its natural width, so
// JS can tell whether it fits a narrower row than the one it is laid out in; past
// one line only the height matters. Trailing whitespace hangs past a line without
// wrapping it, so getLineMax leaves it out.
val layout = editor.layout
val contentHeight = layout?.height ?: editor.measuredHeight
val contentWidth = if (layout?.lineCount == 1) layout.getLineMax(0).toInt() else 0
if (contentHeight == lastContentHeight && contentWidth == lastContentWidth) return
lastContentHeight = contentHeight
lastContentWidth = contentWidth
val density = resources.displayMetrics.density
onComposerContentSizeChange(
mapOf("height" to contentHeight / resources.displayMetrics.density),
mapOf("width" to contentWidth / density, "height" to contentHeight / density),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,28 @@ public final class T3ComposerEditorView: ExpoView, UITextViewDelegate, UITextDro
}

private func emitContentSizeIfNeeded() {
let nextSize = textView.contentSize
// The laid-out text without insets. A one-line draft reports its natural width, so
// JS can tell whether it fits a narrower row than the one it is laid out in; past
// one line only the height matters, so layout stops at the second line. The width
// comes from the line fragment because usedRect spans the container once the view
// scrolls. A trailing newline's empty line only exists as the extra fragment.
let layout = textView.layoutManager
let container = textView.textContainer
var lines = 0
var width: CGFloat = 0
var height: CGFloat = 0
let glyphs = NSRange(location: 0, length: layout.numberOfGlyphs)
layout.enumerateLineFragments(forGlyphRange: glyphs) { _, used, _, _, stop in
lines += 1
width = used.width
height = used.maxY
if lines > 1 { stop.pointee = true }
}
if lines < 2, layout.extraLineFragmentTextContainer === container {
lines += 1
height = max(height, layout.extraLineFragmentUsedRect.maxY)
}
let nextSize = CGSize(width: lines == 1 ? ceil(width) : 0, height: ceil(height))
guard abs(nextSize.width - lastContentSize.width) > 0.5 ||
abs(nextSize.height - lastContentSize.height) > 0.5 else {
return
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"config:preview": "APP_VARIANT=preview expo config",
"config:prod": "APP_VARIANT=production expo config",
"profile:android:hermes": "mkdir -p profiles/review && react-native profile-hermes profiles/review",
"sync:chip-icons": "node scripts/sync-context-chip-icons.mjs",
"sync:pierre-icons": "node scripts/sync-pierre-file-icons.mjs",
"measure-renders": "node scripts/measure-renders.mts",
"generate": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/generate-uniwind-themes.mts",
Expand Down Expand Up @@ -133,7 +134,7 @@
"react-native-pdf-jsi": "5.0.0",
"react-native-reanimated": "4.7.1",
"react-native-safe-area-context": "~5.10.1",
"react-native-screens": "file:deps/react-native-screens-5.0.0-t3.29.tgz",
"react-native-screens": "file:deps/react-native-screens-5.0.0-t3.30.tgz",
"react-native-shiki-engine": "^0.3.12",
"react-native-svg": "15.16.0",
"react-native-webview": "14.0.1",
Expand Down
93 changes: 93 additions & 0 deletions apps/mobile/scripts/sync-context-chip-icons.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as NodeFS from "node:fs";
import * as NodeModule from "node:module";
import * as NodePath from "node:path";
import * as NodeURL from "node:url";

const scriptDirectory = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));
const appDirectory = NodePath.resolve(scriptDirectory, "..");
const repositoryRoot = NodePath.resolve(appDirectory, "../..");
const outputDirectory = NodePath.join(appDirectory, "assets/context-chip-icons");
const generatedModulePath = NodePath.join(appDirectory, "src/lib/contextChipIcons.generated.ts");

const requireFromWeb = NodeModule.createRequire(
NodePath.join(repositoryRoot, "apps/web/package.json"),
);
const requireFromScripts = NodeModule.createRequire(
NodePath.join(repositoryRoot, "scripts/package.json"),
);
const sharp = requireFromScripts("sharp");
const lucideIconsDirectory = NodePath.join(
NodePath.dirname(requireFromWeb.resolve("lucide-react/package.json")),
"dist/esm/icons",
);
const webChipSource = NodeFS.readFileSync(
NodePath.join(repositoryRoot, "apps/web/src/components/composerInlineChip.ts"),
"utf8",
);
const skillSvg = webChipSource.match(/SKILL_CHIP_ICON_SVG = `([\s\S]*?)`;/)?.[1];

if (!skillSvg) {
throw new Error(
"Could not read SKILL_CHIP_ICON_SVG from apps/web/src/components/composerInlineChip.ts",
);
}

const lucideIcons = [
"circle-dashed",
"film",
"git-merge",
"git-pull-request-arrow",
"git-pull-request-closed",
"git-pull-request-draft",
"image",
"message-circle",
"messages-square",
"mouse-pointer-click",
"terminal",
];

async function lucideSvg(name) {
const module = await import(
NodeURL.pathToFileURL(NodePath.join(lucideIconsDirectory, `${name}.js`)).href
);
const body = module.__iconNode
.map(([tag, { key: _key, ...attributes }]) => {
const attributeText = Object.entries(attributes)
.map(([attribute, value]) => `${attribute}="${value}"`)
.join(" ");
return `<${tag} ${attributeText}/>`;
})
.join("");
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${body}</svg>`;
}

async function renderIcon(token, svg) {
const sized = svg
.replace(/^<svg[^>]*?>/, (tag) =>
tag
.replace(/ (?:width|height)="[^"]*"/g, "")
.replace("<svg ", '<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" '),
)
.replaceAll("currentColor", "#ffffff");
await sharp(Buffer.from(sized), { density: 72 * (64 / 24) })
.resize(64, 64)
.png()
.toFile(NodePath.join(outputDirectory, `${token}.png`));
}

NodeFS.rmSync(outputDirectory, { recursive: true, force: true });
NodeFS.mkdirSync(outputDirectory, { recursive: true });

for (const name of lucideIcons) {
await renderIcon(name, await lucideSvg(name));
}
await renderIcon("skill", skillSvg);

const tokens = [...lucideIcons, "skill"].sort();
const generatedSource = `import type { ImageSourcePropType } from "react-native";\n\nexport const CONTEXT_CHIP_ICON_SOURCES = {\n${tokens
.map(
(token) =>
` ${/^\w+$/.test(token) ? token : `"${token}"`}: require("../../assets/context-chip-icons/${token}.png"),`,
)
.join("\n")}\n} as const satisfies Readonly<Record<string, ImageSourcePropType>>;\n`;
NodeFS.writeFileSync(generatedModulePath, generatedSource);
13 changes: 7 additions & 6 deletions apps/mobile/src/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ import { AndroidWorkspaceTabsProvider } from "./features/workspace/AndroidWorksp
import {
PullRequestRouteScreen,
PullRequestFileRouteScreen,
PullRequestFilesRouteScreen,
} from "./features/pull-requests/PullRequestRouteScreen";
import { PullRequestFilesSheet } from "./features/pull-requests/PullRequestFiles";
import { PullRequestReviewSheet } from "./features/pull-requests/PullRequestReviewSheet";
import { PullRequestReviewersScreen } from "./features/pull-requests/PullRequestReviewersScreen";
import {
Expand Down Expand Up @@ -729,12 +729,13 @@ const RootStackConfig = createWorkspaceStackNavigator({
options: DETAIL_HEADER_OPTIONS,
}),
PullRequestFiles: createNativeStackScreen({
screen: PullRequestFilesRouteScreen,
screen: PullRequestFilesSheet,
options: {
...(Platform.OS === "ios" ? SHEET_GLASS_HEADER_OPTIONS : undefined),
...FORM_SHEET_PRESENTATION_OPTIONS,
sheetAllowedDetents: [0.6, 0.92],
sheetGrabberVisible: true,
headerShown: false,
sheetCornerRadius: Platform.OS === "android" ? 28 : undefined,
},
}),
PullRequestReviewers: createNativeStackScreen({
Expand All @@ -756,19 +757,18 @@ const RootStackConfig = createWorkspaceStackNavigator({
options: {
...(Platform.OS === "android"
? { presentation: "fullScreenModal" as const }
: FORM_SHEET_PRESENTATION_OPTIONS),
: { ...SHEET_GLASS_HEADER_OPTIONS, ...FORM_SHEET_PRESENTATION_OPTIONS }),
sheetAllowedDetents: Platform.OS === "android" ? undefined : [0.75, 0.92],
sheetGrabberVisible: Platform.OS !== "android",
headerShown: false,
},
}),
PullRequestReview: createNativeStackScreen({
screen: PullRequestReviewSheet,
options: {
...(Platform.OS === "ios" ? SHEET_GLASS_HEADER_OPTIONS : undefined),
...FORM_SHEET_PRESENTATION_OPTIONS,
sheetAllowedDetents: [0.75, 0.92],
sheetGrabberVisible: true,
headerShown: false,
},
}),
Thread: createNativeStackScreen({
Expand Down Expand Up @@ -900,6 +900,7 @@ const RootStackConfig = createWorkspaceStackNavigator({
screen: GitCommitSheet,
linking: `${THREAD_LINKING_PREFIX}/git/commit`,
options: {
...(Platform.OS === "ios" ? SHEET_GLASS_HEADER_OPTIONS : undefined),
...FORM_SHEET_PRESENTATION_OPTIONS,
sheetAllowedDetents: [0.8, 0.92],
sheetGrabberVisible: true,
Expand Down
Loading