feat: add host-local custom project icons (#2335)

* feat(projects): add local icons and colors

* refactor(projects): simplify appearance handling

* fix(projects): serialize appearance saves

* test(app): update project appearance expectations

* test: fix project appearance CI expectations

* refactor(projects): organize appearance code

* fix(server): adopt main's reconcile model in project appearance updates

* fix(projects): preserve host-local appearance targets

* fix(desktop): restore Linux development startup

Ubuntu can block Electron's unprivileged user-namespace fallback while the npm-installed sandbox helper is not SUID. Disable the Chromium process sandbox only in the Linux development runner so packaged desktop policy remains unchanged.

* feat(projects): add custom project icons

Keep URL acquisition in the client so the host daemon only validates uploaded bytes. Project name and icon changes share one adaptive edit sheet.

* refactor(settings): scope projects by host

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
nikuscs
2026-08-01 01:34:42 +08:00
committed by GitHub
co-authored by Mohamed Boudra
parent 706ae3d9bb
commit 800bd2bef1
67 changed files with 2158 additions and 912 deletions
@@ -75,7 +75,7 @@ import {
useHostProjects,
type HostProjectListItem,
} from "@/projects/host-projects";
import { useProjectIconDataByProjectViewKey } from "@/projects/project-icons";
import { useProjectIcons } from "@/projects/icons";
import { ICON_SIZE, type Theme } from "@/styles/theme";
import type { ComposerAttachment } from "@/attachments/types";
import { useDraftWorkspaceAttachmentScopeKey } from "@/attachments/workspace-attachments-store";
@@ -1578,12 +1578,22 @@ export function NewWorkspaceScreen({
if (!iconWorkingDir) {
return [];
}
return [{ projectViewKey: project.viewKey, serverId: selectedServerId, iconWorkingDir }];
const host = project.hosts.find((candidate) => candidate.serverId === selectedServerId);
if (!host) return [];
return [
{
projectViewKey: project.viewKey,
projectId: host.projectId,
serverId: selectedServerId,
iconWorkingDir,
customIconRevision: host.customIconRevision,
},
];
}),
[projects, selectedServerId],
);
const projectIconDataByProjectViewKey = useProjectIconDataByProjectViewKey({
const projectIconDataByProjectViewKey = useProjectIcons({
projects: projectIconTargets,
});
const draftKey = buildNewWorkspaceDraftKey(draftId);