Merge branch 'main' into dev

Resolve 38 conflicts preserving both sides' work:
- Server: DB-based registries (dev) + WorkspaceGitService/DaemonConfigStore (main)
- App: script proxy/voice MCP (dev) + pin/hide tabs/bootstrap boundary (main)
- Shared: WorkspaceScriptPayload (dev) + git/GitHub runtime schemas (main)
- Deleted workspace-registry-bootstrap.ts (dev's DB approach supersedes)
This commit is contained in:
Mohamed Boudra
2026-04-14 03:05:45 +07:00
333 changed files with 13490 additions and 6317 deletions
@@ -4,10 +4,10 @@ import {
mergeWorkspaceSnapshotWithExisting,
normalizeWorkspaceDescriptor,
useSessionStore,
type WorkspaceDescriptor,
} from "@/stores/session-store";
import { getHostRuntimeStore } from "@/runtime/host-runtime";
import { useSidebarOrderStore } from "@/stores/sidebar-order-store";
import type { WorkspaceDescriptor } from "@/stores/session-store";
import { projectDisplayNameFromProjectId } from "@/utils/project-display-name";
const EMPTY_ORDER: string[] = [];
@@ -25,7 +25,6 @@ export interface SidebarWorkspaceEntry {
projectKind: WorkspaceDescriptor["projectKind"];
workspaceKind: WorkspaceDescriptor["workspaceKind"];
name: string;
activityAt: Date | null;
statusBucket: SidebarStateBucket;
diffStat: { additions: number; deletions: number } | null;
scripts: WorkspaceDescriptor["scripts"];
@@ -40,7 +39,6 @@ export interface SidebarProjectEntry {
statusBucket: SidebarStateBucket;
activeCount: number;
totalWorkspaces: number;
latestActivityAt: Date | null;
workspaces: SidebarWorkspaceEntry[];
}
@@ -73,15 +71,6 @@ function compareWorkspaceBaseline(
left: SidebarWorkspaceEntry,
right: SidebarWorkspaceEntry,
): number {
if (left.activityAt && right.activityAt) {
const dateDelta = right.activityAt.getTime() - left.activityAt.getTime();
if (dateDelta !== 0) {
return dateDelta;
}
} else if (left.activityAt || right.activityAt) {
return left.activityAt ? -1 : 1;
}
const nameDelta = left.name.localeCompare(right.name, undefined, {
numeric: true,
sensitivity: "base",
@@ -97,15 +86,6 @@ function compareWorkspaceBaseline(
}
function compareProjectBaseline(left: SidebarProjectEntry, right: SidebarProjectEntry): number {
if (left.latestActivityAt && right.latestActivityAt) {
const dateDelta = right.latestActivityAt.getTime() - left.latestActivityAt.getTime();
if (dateDelta !== 0) {
return dateDelta;
}
} else if (left.latestActivityAt || right.latestActivityAt) {
return left.latestActivityAt ? -1 : 1;
}
return left.projectName.localeCompare(right.projectName, undefined, {
numeric: true,
sensitivity: "base",
@@ -132,7 +112,6 @@ export function buildSidebarProjectsFromWorkspaces(input: {
statusBucket: "done",
activeCount: 0,
totalWorkspaces: 0,
latestActivityAt: null,
workspaces: [],
} satisfies SidebarProjectEntry);
@@ -145,7 +124,6 @@ export function buildSidebarProjectsFromWorkspaces(input: {
projectKind: workspace.projectKind,
workspaceKind: workspace.workspaceKind,
name: workspace.name,
activityAt: workspace.activityAt,
statusBucket: workspace.status,
diffStat: workspace.diffStat,
scripts: workspace.scripts,
@@ -158,12 +136,6 @@ export function buildSidebarProjectsFromWorkspaces(input: {
project.activeCount += 1;
}
project.statusBucket = aggregateBucket(project.statusBucket, workspace.status);
if (
!project.latestActivityAt ||
(workspace.activityAt && workspace.activityAt.getTime() > project.latestActivityAt.getTime())
) {
project.latestActivityAt = workspace.activityAt;
}
byProject.set(workspace.projectId, project);
}
@@ -262,23 +234,9 @@ function getWorkspaceOrderScopeKey(serverId: string, projectKey: string): string
return `${serverId.trim()}::${projectKey.trim()}`;
}
function toWorkspaceDescriptor(payload: {
id: string;
projectId: string;
projectDisplayName: string;
projectRootPath: string;
workspaceDirectory: string;
projectKind: WorkspaceDescriptor["projectKind"];
workspaceKind: WorkspaceDescriptor["workspaceKind"];
name: string;
status: WorkspaceDescriptor["status"];
activityAt: string | null;
scripts?: WorkspaceDescriptorPayload["scripts"];
}): WorkspaceDescriptor {
return normalizeWorkspaceDescriptor({
...payload,
scripts: payload.scripts ?? [],
});
function toWorkspaceDescriptor(payload: WorkspaceDescriptorPayload): WorkspaceDescriptor {
return normalizeWorkspaceDescriptor(payload);
}
export function useSidebarWorkspacesList(options?: {