Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
28107e8
chore: remove `AnnotatableFileDiff` leftovers, rename file (#3488)
ipanasenko Jun 22, 2026
ea52bb1
[codex] fix: guard trace ID clipboard copy (#3505)
StiensWout Jun 22, 2026
8b993bc
[codex] fix: restore pending input keyboard activation (#3501)
StiensWout Jun 22, 2026
c1e2408
[codex] fix: preserve localhost preview hosts (#3499)
StiensWout Jun 22, 2026
8919ae7
[codex] Reject unsupported remote pairing protocols (#3498)
StiensWout Jun 22, 2026
37ac970
Persist mobile composer selectors across drafts (#3496)
juliusmarminge Jun 22, 2026
f5f98cf
Stabilize composer provider state while typing (#3507)
cursor[bot] Jun 22, 2026
fb10345
feat: add persistent word-wrap setting for chat code blocks and table…
imabdulazeez Jun 22, 2026
b2d17b7
Add main sidebar toggle (#3497)
juliusmarminge Jun 22, 2026
8cc9a6f
[codex] Restore T3 Connect account controls (#3492)
juliusmarminge Jun 22, 2026
92e54fb
[codex] fix: guard DPoP fallback URL construction (#3503)
StiensWout Jun 22, 2026
6672a1d
Bump Clerk packages and refresh lockfile (#3511)
juliusmarminge Jun 22, 2026
3083d71
[codex] fix: clarify Cursor CLI setup error (#3519)
StiensWout Jun 23, 2026
4abf8b4
[codex] fix: ignore stale shell reducer events (#3517)
StiensWout Jun 23, 2026
c6c6491
Reduce ChatMarkdown settings rerenders (#3536)
cursor[bot] Jun 23, 2026
a4964b3
[codex] fix: show standalone element-pick context (#3527)
StiensWout Jun 23, 2026
2a653c2
sync: merge upstream final UI runtime updates
Jun 23, 2026
3a68825
Fix stack CI diagnostics
Jun 24, 2026
847a90d
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
f4897c3
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
c1e77fe
Fix final runtime test imports
Jun 24, 2026
c2c4a7f
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
6372096
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
f6aa458
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
5ca7dd9
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
c13ad63
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
39b3bce
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
ef2651b
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
c63875b
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
1eb0dd8
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 2026
fd85a5c
Merge branch 'sync/upstream-20260621-runtime-diagnostics' into sync/u…
Jun 24, 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
2 changes: 1 addition & 1 deletion apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const clientSettings: ClientSettings = {
confirmThreadDelete: false,
dismissedProviderUpdateNotificationKeys: [],
diffIgnoreWhitespace: true,
diffWordWrap: true,
favorites: [],
providerModelPreferences: {},
sidebarProjectGroupingMode: "repository_path",
Expand All @@ -29,6 +28,7 @@ const clientSettings: ClientSettings = {
sidebarThreadSortOrder: "created_at",
sidebarThreadPreviewCount: 6,
timestampFormat: "24-hour",
wordWrap: true,
};

const decodeClientSettingsJson = Schema.decodeEffect(Schema.fromJsonString(ClientSettingsSchema));
Expand Down
64 changes: 45 additions & 19 deletions apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import {
resolveProviderOptionDescriptors,
} from "../../lib/providerOptions";
import { buildThreadRoutePath } from "../../lib/routes";
import { scopedProjectKey } from "../../lib/scopedEntities";
import { MOBILE_TYPOGRAPHY } from "../../lib/typography";
import { getComposerDraftSnapshot } from "../../state/use-composer-drafts";
import { useProjects } from "../../state/entities";
import { branchBadgeLabel, useNewTaskFlow } from "./new-task-flow-provider";
import { useCreateProjectThread } from "./use-project-actions";
Expand Down Expand Up @@ -63,6 +65,7 @@ export function NewTaskDraftScreen(props: {
const controlsBottomPadding = isKeyboardVisible ? 8 : Math.max(insets.bottom, 10);
const { logicalProjects, selectedProject, setProject } = flow;
const promptInputRef = useRef<ComposerEditorHandle>(null);
const loadedBranchesProjectKeyRef = useRef<string | null>(null);

const borderColor = useThemeColor("--color-border");
const sheetFadeOpaque = colorScheme === "dark" ? "rgba(14,14,14,0.98)" : "rgba(242,242,247,0.98)";
Expand All @@ -78,6 +81,12 @@ export function NewTaskDraftScreen(props: {
) ?? null;

if (directProject) {
if (
selectedProject?.environmentId === directProject.environmentId &&
selectedProject.id === directProject.id
) {
return;
}
setProject(directProject);
return;
}
Expand Down Expand Up @@ -105,10 +114,16 @@ export function NewTaskDraftScreen(props: {

useEffect(() => {
if (!selectedProject) {
loadedBranchesProjectKeyRef.current = null;
return;
}
const projectKey = `${selectedProject.environmentId}:${selectedProject.id}`;
if (loadedBranchesProjectKeyRef.current === projectKey) {
return;
}
loadedBranchesProjectKeyRef.current = projectKey;
void flow.loadBranches();
}, [flow, selectedProject]);
}, [flow.loadBranches, selectedProject]);

useEffect(() => {
if (!selectedProject) {
Expand Down Expand Up @@ -292,11 +307,7 @@ export function NewTaskDraftScreen(props: {
if (!event.startsWith("model:")) {
return;
}
// Defer state update so the native menu dismiss animation completes
// before re-rendering the menu actions (prevents submenu jump).
setTimeout(() => {
flow.setSelectedModelKey(event.slice("model:".length));
}, 150);
flow.setSelectedModelKey(event.slice("model:".length));
}

function handleEnvironmentMenuAction(event: string) {
Expand Down Expand Up @@ -366,27 +377,42 @@ export function NewTaskDraftScreen(props: {
);

async function handleStart(): Promise<void> {
const selectedProject = flow.selectedProject;
if (!selectedProject) {
return;
}
const draft = getComposerDraftSnapshot(
`new-task:${scopedProjectKey(selectedProject.environmentId, selectedProject.id)}`,
);
const modelSelection = draft.modelSelection ?? flow.selectedModel;
const workspaceMode = draft.workspaceSelection?.mode ?? flow.workspaceMode;
const selectedBranchName = draft.workspaceSelection?.branch ?? flow.selectedBranchName;
const selectedWorktreePath =
draft.workspaceSelection?.worktreePath ?? flow.selectedWorktreePath;
const runtimeMode = draft.runtimeMode ?? flow.runtimeMode;
const interactionMode = draft.interactionMode ?? flow.interactionMode;
const initialMessageText = draft.text.trim();

if (
!flow.selectedProject ||
!flow.selectedModel ||
flow.prompt.trim().length === 0 ||
!modelSelection ||
initialMessageText.length === 0 ||
flow.submitting ||
(flow.workspaceMode === "worktree" && !flow.selectedBranchName)
(workspaceMode === "worktree" && !selectedBranchName)
) {
return;
}

flow.setSubmitting(true);
const result = await createProjectThread({
project: flow.selectedProject,
modelSelection: flow.selectedModel,
envMode: flow.workspaceMode,
branch: flow.selectedBranchName,
worktreePath: flow.workspaceMode === "worktree" ? null : flow.selectedWorktreePath,
runtimeMode: flow.runtimeMode,
interactionMode: flow.interactionMode,
initialMessageText: flow.prompt.trim(),
initialAttachments: flow.attachments,
project: selectedProject,
modelSelection,
envMode: workspaceMode,
branch: selectedBranchName,
worktreePath: workspaceMode === "worktree" ? null : selectedWorktreePath,
runtimeMode,
interactionMode,
initialMessageText,
initialAttachments: draft.attachments,
});
flow.setSubmitting(false);

Expand Down
85 changes: 17 additions & 68 deletions apps/mobile/src/features/threads/ThreadRouteScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
import { useCallback, useMemo, useState } from "react";
import * as Option from "effect/Option";
import {
EnvironmentId,
type ModelSelection,
type ProjectScript,
type ProviderInteractionMode,
type RuntimeMode,
} from "@t3tools/contracts";
import { EnvironmentId, type ProjectScript } from "@t3tools/contracts";
import { projectScriptCwd, projectScriptRuntimeEnv } from "@t3tools/shared/projectScripts";
import { Pressable, ScrollView, Text as RNText, View } from "react-native";
import { useWorkspaceState } from "../../state/workspace";
Expand Down Expand Up @@ -79,18 +73,6 @@ export function ThreadRouteScreen() {
const gitState = useSelectedThreadGitState();
const gitActions = useSelectedThreadGitActions();
const requests = useSelectedThreadRequests();
const updateThreadMetadata = useAtomCommand(
threadEnvironment.updateMetadata,
"thread metadata update",
);
const setThreadRuntimeMode = useAtomCommand(
threadEnvironment.setRuntimeMode,
"thread runtime mode",
);
const setThreadInteractionMode = useAtomCommand(
threadEnvironment.setInteractionMode,
"thread interaction mode",
);
const interruptThreadTurn = useAtomCommand(threadEnvironment.interruptTurn, "thread interrupt");
const router = useRouter();
const params = useLocalSearchParams<{
Expand All @@ -105,6 +87,18 @@ export function ThreadRouteScreen() {
const routeConnectionState =
routeEnvironmentRuntime?.connectionState ?? (environmentId ? "available" : connectionState);
const routeConnectionError = routeEnvironmentRuntime?.connectionError ?? null;
const selectedThreadWithDraftSettings = useMemo(
() =>
selectedThread
? {
...selectedThread,
modelSelection: composer.modelSelection ?? selectedThread.modelSelection,
runtimeMode: composer.runtimeMode ?? selectedThread.runtimeMode,
interactionMode: composer.interactionMode ?? selectedThread.interactionMode,
}
: null,
[composer.interactionMode, composer.modelSelection, composer.runtimeMode, selectedThread],
);

/* ─── Native header theming ──────────────────────────────────────── */
const iconColor = String(useThemeColor("--color-icon"));
Expand Down Expand Up @@ -157,51 +151,6 @@ export function ThreadRouteScreen() {
const handleOpenConnectionEditor = useCallback(() => {
void router.push("/connections");
}, [router]);
const handleUpdateThreadModelSelection = useCallback(
(modelSelection: ModelSelection) => {
if (!selectedThread) {
return;
}
return updateThreadMetadata({
environmentId: selectedThread.environmentId,
input: {
threadId: selectedThread.id,
modelSelection,
},
});
},
[selectedThread, updateThreadMetadata],
);
const handleUpdateThreadRuntimeMode = useCallback(
(runtimeMode: RuntimeMode) => {
if (!selectedThread) {
return;
}
return setThreadRuntimeMode({
environmentId: selectedThread.environmentId,
input: {
threadId: selectedThread.id,
runtimeMode,
},
});
},
[selectedThread, setThreadRuntimeMode],
);
const handleUpdateThreadInteractionMode = useCallback(
(interactionMode: ProviderInteractionMode) => {
if (!selectedThread) {
return;
}
return setThreadInteractionMode({
environmentId: selectedThread.environmentId,
input: {
threadId: selectedThread.id,
interactionMode,
},
});
},
[selectedThread, setThreadInteractionMode],
);
const handleStopThread = useCallback(() => {
if (
!selectedThread ||
Expand Down Expand Up @@ -435,7 +384,7 @@ export function ThreadRouteScreen() {

<View className="flex-1 bg-screen">
<ThreadDetailScreen
selectedThread={selectedThread}
selectedThread={selectedThreadWithDraftSettings ?? selectedThread}
contentPresentation={contentPresentation}
screenTone={connectionTone(routeConnectionState)}
connectionError={routeConnectionError}
Expand Down Expand Up @@ -466,9 +415,9 @@ export function ThreadRouteScreen() {
onStopThread={handleStopThread}
onSendMessage={composer.onSendMessage}
onReconnectEnvironment={handleReconnectEnvironment}
onUpdateThreadModelSelection={handleUpdateThreadModelSelection}
onUpdateThreadRuntimeMode={handleUpdateThreadRuntimeMode}
onUpdateThreadInteractionMode={handleUpdateThreadInteractionMode}
onUpdateThreadModelSelection={composer.onUpdateModelSelection}
onUpdateThreadRuntimeMode={composer.onUpdateRuntimeMode}
onUpdateThreadInteractionMode={composer.onUpdateInteractionMode}
onRespondToApproval={requests.onRespondToApproval}
onSelectUserInputOption={requests.onSelectUserInputOption}
onChangeUserInputCustomAnswer={requests.onChangeUserInputCustomAnswer}
Expand Down
Loading
Loading