diff options
| author | Adam <[email protected]> | 2026-01-19 14:59:41 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-19 14:59:47 -0600 |
| commit | a4d1824412c57d733c33c58e19551f0818c82e8a (patch) | |
| tree | 8530a4594b6d760ad03f775ecc96dd8324b9c410 | |
| parent | cac35bc52d9a8a2c9ca673510b2266d1c13ee141 (diff) | |
| download | opencode-a4d1824412c57d733c33c58e19551f0818c82e8a.tar.gz opencode-a4d1824412c57d733c33c58e19551f0818c82e8a.zip | |
fix(app): no more favicons
| -rw-r--r-- | packages/app/src/components/dialog-edit-project.tsx | 4 | ||||
| -rw-r--r-- | packages/app/src/context/layout.tsx | 62 | ||||
| -rw-r--r-- | packages/app/src/pages/layout.tsx | 2 | ||||
| -rw-r--r-- | packages/opencode/src/project/project.ts | 4 | ||||
| -rw-r--r-- | packages/sdk/js/src/v2/gen/sdk.gen.ts | 1 | ||||
| -rw-r--r-- | packages/sdk/js/src/v2/gen/types.gen.ts | 2 |
6 files changed, 51 insertions, 24 deletions
diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx index 091f00702..7acb766f8 100644 --- a/packages/app/src/components/dialog-edit-project.tsx +++ b/packages/app/src/components/dialog-edit-project.tsx @@ -22,7 +22,7 @@ export function DialogEditProject(props: { project: LocalProject }) { const [store, setStore] = createStore({ name: defaultName(), color: props.project.icon?.color || "pink", - iconUrl: props.project.icon?.url || "", + iconUrl: props.project.icon?.override || "", saving: false, }) @@ -74,7 +74,7 @@ export function DialogEditProject(props: { project: LocalProject }) { await globalSDK.client.project.update({ projectID: props.project.id, name, - icon: { color: store.color, url: store.iconUrl }, + icon: { color: store.color, override: store.iconUrl }, }) setStore("saving", false) dialog.close() diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index a8a8ce1e9..d7d09aa39 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -208,10 +208,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }) }) - const usedColors = new Set<AvatarColorKey>() + const [colors, setColors] = createStore<Record<string, AvatarColorKey>>({}) - function pickAvailableColor(): AvatarColorKey { - const available = AVATAR_COLOR_KEYS.filter((c) => !usedColors.has(c)) + function pickAvailableColor(used: Set<string>): AvatarColorKey { + const available = AVATAR_COLOR_KEYS.filter((c) => !used.has(c)) if (available.length === 0) return AVATAR_COLOR_KEYS[Math.floor(Math.random() * AVATAR_COLOR_KEYS.length)] return available[Math.floor(Math.random() * available.length)] } @@ -222,24 +222,15 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( const metadata = projectID ? globalSync.data.project.find((x) => x.id === projectID) : globalSync.data.project.find((x) => x.worktree === project.worktree) - return [ - { - ...(metadata ?? {}), - ...project, - icon: { url: metadata?.icon?.url, color: metadata?.icon?.color }, + return { + ...(metadata ?? {}), + ...project, + icon: { + url: metadata?.icon?.url, + override: metadata?.icon?.override, + color: metadata?.icon?.color, }, - ] - } - - function colorize(project: LocalProject) { - if (project.icon?.color) return project - const color = pickAvailableColor() - usedColors.add(color) - project.icon = { ...project.icon, color } - if (project.id) { - globalSdk.client.project.update({ projectID: project.id, icon: { color } }) } - return project } const roots = createMemo(() => { @@ -277,8 +268,37 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }) }) - const enriched = createMemo(() => server.projects.list().flatMap(enrich)) - const list = createMemo(() => enriched().flatMap(colorize)) + const enriched = createMemo(() => server.projects.list().map(enrich)) + const list = createMemo(() => { + const projects = enriched() + return projects.map((project) => { + const color = project.icon?.color ?? colors[project.worktree] + if (!color) return project + const icon = project.icon ? { ...project.icon, color } : { color } + return { ...project, icon } + }) + }) + + createEffect(() => { + const projects = enriched() + if (projects.length === 0) return + + const used = new Set<string>() + for (const project of projects) { + const color = project.icon?.color ?? colors[project.worktree] + if (color) used.add(color) + } + + for (const project of projects) { + if (project.icon?.color) continue + if (colors[project.worktree]) continue + const color = pickAvailableColor(used) + used.add(color) + setColors(project.worktree, color) + if (!project.id) continue + void globalSdk.client.project.update({ projectID: project.id, icon: { color } }) + } + }) onMount(() => { Promise.all( diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index a8f9b162f..9daac949e 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -1284,7 +1284,7 @@ export default function Layout(props: ParentProps) { <div class="size-full rounded overflow-clip"> <Avatar fallback={name()} - src={props.project.id === opencode ? "https://opencode.ai/favicon-v2.svg" : props.project.icon?.url} + src={props.project.id === opencode ? "https://opencode.ai/favicon-v2.svg" : props.project.icon?.override} {...getAvatarColors(props.project.icon?.color)} class="size-full rounded" style={ diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 2cec78623..40ebb21ea 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -25,6 +25,7 @@ export namespace Project { icon: z .object({ url: z.string().optional(), + override: z.string().optional(), color: z.string().optional(), }) .optional(), @@ -190,6 +191,7 @@ export namespace Project { if (!existing.sandboxes) existing.sandboxes = [] if (Flag.OPENCODE_EXPERIMENTAL_ICON_DISCOVERY) discover(existing) + const result: Info = { ...existing, worktree, @@ -213,6 +215,7 @@ export namespace Project { export async function discover(input: Info) { if (input.vcs !== "git") return + if (input.icon?.override) return if (input.icon?.url) return const glob = new Bun.Glob("**/{favicon}.{ico,png,svg,jpg,jpeg,webp}") const matches = await Array.fromAsync( @@ -293,6 +296,7 @@ export namespace Project { ...draft.icon, } if (input.icon.url !== undefined) draft.icon.url = input.icon.url + if (input.icon.override !== undefined) draft.icon.override = input.icon.override || undefined if (input.icon.color !== undefined) draft.icon.color = input.icon.color } draft.time.updated = Date.now() diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 59b7f0696..706d0f9c2 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -302,6 +302,7 @@ export class Project extends HeyApiClient { name?: string icon?: { url?: string + override?: string color?: string } }, diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 75540f907..b7e72fbad 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -25,6 +25,7 @@ export type Project = { name?: string icon?: { url?: string + override?: string color?: string } time: { @@ -2229,6 +2230,7 @@ export type ProjectUpdateData = { name?: string icon?: { url?: string + override?: string color?: string } } |
