Fix compact composer controls and native scrolling (#2361)

* fix(app): refine compact composer controls and native scrolling

Consolidate responsive agent controls and model browsing inside the compact sheet. Preserve manual native scrolling by suspending sticky bottom maintenance while the user owns the viewport.

* fix(app): suppress recoverable resume refresh errors

Resume revalidation can race host reconnection and reject while the host is offline. Treat the refresh as deferred so development LogBox does not surface a recoverable disconnect.

* fix(app): stabilize compact model sheet loading

Let the model list consume the sheet space above persistent controls as the sheet expands. Keep unresolved defaults in a loading state so Select model only represents a genuinely empty selection.

* fix(app): preserve route anchors during native scroll

* fix(app): preserve native scroll intent and sheet dismissal

* fix(app): keep compact sheet styles composable
This commit is contained in:
Mohamed Boudra
2026-07-24 00:24:10 +08:00
committed by GitHub
parent 8cf70d10bf
commit 8e063f0dfc
34 changed files with 3252 additions and 1806 deletions
@@ -260,6 +260,25 @@ describe("combined model selector data", () => {
).toBe("Default");
});
it("distinguishes a loading selection from a resolved empty selection", () => {
expect(
resolveSelectedModelLabel({
providers: [],
selectedProvider: "",
selectedModel: "",
isLoading: true,
}),
).toBe("Loading...");
expect(
resolveSelectedModelLabel({
providers: [],
selectedProvider: "",
selectedModel: "",
isLoading: false,
}),
).toBe("Select model");
});
it("keeps a stored selected model visible when current snapshot rows no longer offer it", () => {
const providers = buildSelectableProviderSelectorProviders([
snapshotEntry({
@@ -157,7 +157,9 @@ export function resolveSelectedModelLabel(input: {
}): string {
const selectedProvider = input.selectedProvider.trim();
if (!selectedProvider) {
return i18n.t("providerSelection.selectModel");
return input.isLoading
? i18n.t("providerSelection.loading")
: i18n.t("providerSelection.selectModel");
}
const provider = input.providers.find((entry) => entry.id === selectedProvider);
@@ -1096,7 +1096,7 @@ describe("resolveAgentForm", () => {
});
describe("RESET", () => {
it("resets userModified flags while keeping form state", () => {
it("keeps form values but marks them unresolved for the next open", () => {
const state = makeState(
{ provider: "codex", modeId: "full-access", model: "gpt-5.3-codex" },
{ provider: true, modeId: true, model: true },
@@ -1106,7 +1106,7 @@ describe("resolveAgentForm", () => {
expect(next.userModified).toEqual(INITIAL_USER_MODIFIED);
expect(next.form).toEqual(state.form);
expect(next.resolution.status).toBe("completed");
expect(next.resolution.status).toBe("pending");
});
});
@@ -56,8 +56,8 @@ export const INITIAL_USER_MODIFIED: UserModifiedFields = {
workingDir: false,
};
export const INITIAL_AGENT_FORM_RESOLUTION: AgentFormResolutionState = { status: "completed" };
export const PENDING_AGENT_FORM_RESOLUTION: AgentFormResolutionState = { status: "pending" };
export const INITIAL_AGENT_FORM_RESOLUTION = PENDING_AGENT_FORM_RESOLUTION;
type ProviderPrefs = NonNullable<FormPreferences["providerPreferences"]>[AgentProvider];