Merge origin/main into dev (second round)

Resolve 21 conflicts from latest main (provider profiles, Windows fixes, 0.1.55-rc.1).
Fix all type errors and syntax issues from merge artifacts.
This commit is contained in:
Mohamed Boudra
2026-04-14 04:07:36 +07:00
249 changed files with 9118 additions and 4973 deletions
+4 -4
View File
@@ -1,11 +1,11 @@
import { create } from "zustand";
import { Platform } from "react-native";
import { File as FSFile, Paths } from "expo-file-system";
import * as LegacyFileSystem from "expo-file-system/legacy";
import * as Sharing from "expo-sharing";
import type { HostProfile } from "@/types/host-connection";
import { buildDaemonWebSocketUrl } from "@/utils/daemon-endpoints";
import { openExternalUrl } from "@/utils/open-external-url";
import { isWeb } from "@/constants/platform";
interface DownloadProgress {
percent: number;
@@ -97,10 +97,10 @@ export const useDownloadStore = create<DownloadState>()((set, get) => ({
const downloadUrl = buildDownloadUrl(
downloadTarget.baseUrl,
tokenResponse.token,
Platform.OS === "web" ? downloadTarget.authCredentials : null,
isWeb ? downloadTarget.authCredentials : null,
);
if (Platform.OS === "web") {
if (isWeb) {
triggerBrowserDownload(downloadUrl, resolvedFileName);
get().completeDownload(id);
return;
@@ -153,7 +153,7 @@ export const useDownloadStore = create<DownloadState>()((set, get) => ({
}
} catch (error) {
const message = error instanceof Error ? error.message : "Failed to download file.";
if (Platform.OS === "web") {
if (isWeb) {
console.warn("[DownloadStore] Download failed:", message);
get().failDownload(id, message);
return;
+4 -8
View File
@@ -1,7 +1,6 @@
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { Platform } from "react-native";
import {
buildExplorerCheckoutKey,
coerceExplorerTabForCheckout,
@@ -9,6 +8,7 @@ import {
resolveExplorerTabForCheckout,
type ExplorerTab,
} from "./explorer-tab-memory";
import { isWeb } from "@/constants/platform";
export type { ExplorerTab } from "./explorer-tab-memory";
/**
@@ -128,7 +128,7 @@ function resolveExplorerTabFromActiveCheckout(state: PanelState): ExplorerTab |
});
}
const DEFAULT_DESKTOP_OPEN = Platform.OS === "web";
const DEFAULT_DESKTOP_OPEN = isWeb;
export const usePanelStore = create<PanelState>()(
persist(
@@ -318,11 +318,7 @@ export const usePanelStore = create<PanelState>()(
const state = persistedState as Partial<PanelState> & Record<string, unknown>;
if (version < 2) {
if (
Platform.OS === "web" &&
typeof state.explorerWidth === "number" &&
state.explorerWidth === 400
) {
if (isWeb && typeof state.explorerWidth === "number" && state.explorerWidth === 400) {
state.explorerWidth = DEFAULT_EXPLORER_SIDEBAR_WIDTH;
}
@@ -337,7 +333,7 @@ export const usePanelStore = create<PanelState>()(
if (version < 3) {
if (
Platform.OS === "web" &&
isWeb &&
typeof state.explorerWidth === "number" &&
(state.explorerWidth === 400 || state.explorerWidth === 520)
) {
@@ -46,10 +46,7 @@ describe("provider-recency-store", () => {
useProviderRecencyStore.getState().recordUsage("opencode");
useProviderRecencyStore.getState().recordUsage("codex");
expect(useProviderRecencyStore.getState().recentProviderIds).toEqual([
"codex",
"opencode",
]);
expect(useProviderRecencyStore.getState().recentProviderIds).toEqual(["codex", "opencode"]);
});
it("filters invalid and duplicate providers during migration", () => {
@@ -67,7 +67,9 @@ export function sortProvidersByRecency<T extends { id: string }>(
.map((entry) => entry.provider);
}
function migratePersistedState(state: unknown): Pick<ProviderRecencyStoreState, "recentProviderIds"> {
function migratePersistedState(
state: unknown,
): Pick<ProviderRecencyStoreState, "recentProviderIds"> {
const record = state as { recentProviderIds?: string[] } | null | undefined;
return {
recentProviderIds: sanitizeRecentProviderIds(record?.recentProviderIds),
@@ -110,7 +110,6 @@ interface OpenTabInLayoutResult {
tabId: string;
}
interface RetargetTabInLayoutInput {
layout: WorkspaceLayout;
tabId: string;
@@ -802,7 +801,8 @@ function replaceTabInTree(
}
: tab,
),
focusedTabId: node.pane.focusedTabId === input.tabId ? input.nextTabId : node.pane.focusedTabId,
focusedTabId:
node.pane.focusedTabId === input.tabId ? input.nextTabId : node.pane.focusedTabId,
}),
};
});
@@ -1388,7 +1388,9 @@ function isEntityTarget(
return target.kind === "agent" || target.kind === "terminal";
}
function isAgentTab(tab: WorkspaceTab): tab is WorkspaceTab & { target: { kind: "agent"; agentId: string } } {
function isAgentTab(
tab: WorkspaceTab,
): tab is WorkspaceTab & { target: { kind: "agent"; agentId: string } } {
return tab.target.kind === "agent";
}
@@ -1398,7 +1400,10 @@ function isTerminalTab(
return tab.target.kind === "terminal";
}
function openEntityTabWithoutFocusing(layout: WorkspaceLayout, target: WorkspaceTabTarget): WorkspaceLayout {
function openEntityTabWithoutFocusing(
layout: WorkspaceLayout,
target: WorkspaceTabTarget,
): WorkspaceLayout {
const internalLayout = asInternalLayout(layout);
const focusedPane =
findPaneById(internalLayout.root, internalLayout.focusedPaneId) ??
@@ -1445,7 +1450,9 @@ export function reconcileWorkspaceTabs(
}
const initialTabs = collectAllTabs(nextLayout.root);
const representedAgentIds = new Set(initialTabs.filter(isAgentTab).map((tab) => tab.target.agentId));
const representedAgentIds = new Set(
initialTabs.filter(isAgentTab).map((tab) => tab.target.agentId),
);
const entityGroups = new Map<
string,
@@ -1482,10 +1489,7 @@ export function reconcileWorkspaceTabs(
if (group.tabs.some((tab) => tab.tabId === originalFocusedTabId)) {
reconciledFocusedTabId = canonicalTabId;
}
if (
keeper.tabId !== canonicalTabId ||
!workspaceTabTargetsEqual(keeper.target, group.target)
) {
if (keeper.tabId !== canonicalTabId || !workspaceTabTargetsEqual(keeper.target, group.target)) {
nextLayout = {
root: replaceTabInTree(asInternalLayout(nextLayout).root, {
tabId: keeper.tabId,
@@ -1515,7 +1519,11 @@ export function reconcileWorkspaceTabs(
tabId: tab.tabId,
}) ?? nextLayout;
}
if (isTerminalTab(tab) && snapshot.terminalsHydrated && !standaloneTerminalIds.has(tab.target.terminalId)) {
if (
isTerminalTab(tab) &&
snapshot.terminalsHydrated &&
!standaloneTerminalIds.has(tab.target.terminalId)
) {
nextLayout =
closeTabInLayout({
layout: nextLayout,
@@ -393,8 +393,9 @@ describe("workspace-layout-store actions", () => {
});
it("retargetTab closes a draft tab and focuses the existing canonical target tab", () => {
vi.spyOn(globalThis.crypto, "randomUUID")
.mockReturnValueOnce("55555555-5555-5555-5555-555555555555");
vi.spyOn(globalThis.crypto, "randomUUID").mockReturnValueOnce(
"55555555-5555-5555-5555-555555555555",
);
const workspaceKey = createWorkspaceKey();
const store = useWorkspaceLayoutStore.getState();
@@ -432,12 +433,18 @@ describe("workspace-layout-store actions", () => {
const workspaceKey = createWorkspaceKey();
const store = useWorkspaceLayoutStore.getState();
const firstDraftTabId = store.openTab(workspaceKey, { kind: "draft", draftId: "draft-agent-1" });
const firstDraftTabId = store.openTab(workspaceKey, {
kind: "draft",
draftId: "draft-agent-1",
});
const firstAgentTabId = store.retargetTab(workspaceKey, firstDraftTabId!, {
kind: "agent",
agentId: "agent-1",
});
const secondDraftTabId = store.openTab(workspaceKey, { kind: "draft", draftId: "draft-agent-2" });
const secondDraftTabId = store.openTab(workspaceKey, {
kind: "draft",
draftId: "draft-agent-2",
});
const nextTabId = store.retargetTab(workspaceKey, secondDraftTabId!, {
kind: "agent",
@@ -33,10 +33,7 @@ interface WorkspaceSetupStoreState {
clearServer: (serverId: string) => void;
}
function buildWorkspaceSetupKey(input: {
serverId: string;
workspaceId: string;
}): string | null {
function buildWorkspaceSetupKey(input: { serverId: string; workspaceId: string }): string | null {
return buildWorkspaceTabPersistenceKey(input);
}