summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-09 15:21:04 -0600
committerAdam <[email protected]>2025-12-09 15:21:47 -0600
commit1fbd7a7f9a611749768168ea9a30f87d7eda2c5b (patch)
treef0ec9c556954d86948ebfea43486df71d0ba3f05
parentd7563d16944316a537cf49a6dc7875addaf5dba9 (diff)
downloadopencode-1fbd7a7f9a611749768168ea9a30f87d7eda2c5b.tar.gz
opencode-1fbd7a7f9a611749768168ea9a30f87d7eda2c5b.zip
wip(desktop): progress
-rw-r--r--packages/desktop/src/pages/layout.tsx49
-rw-r--r--packages/ui/src/components/avatar.css5
-rw-r--r--packages/ui/src/components/avatar.tsx10
3 files changed, 39 insertions, 25 deletions
diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx
index c2755b9dc..c083fbdfe 100644
--- a/packages/desktop/src/pages/layout.tsx
+++ b/packages/desktop/src/pages/layout.tsx
@@ -140,8 +140,8 @@ export default function Layout(props: ParentProps) {
return <></>
}
- const ProjectVisual = (props: { directory: string; class?: string }): JSX.Element => {
- const name = createMemo(() => getFilename(props.directory))
+ const ProjectVisual = (props: { project: Project & { expanded: boolean }; class?: string }): JSX.Element => {
+ const name = createMemo(() => getFilename(props.project.worktree))
return (
<Switch>
<Match when={layout.sidebar.opened()}>
@@ -153,7 +153,12 @@ export default function Layout(props: ParentProps) {
>
<div class="flex items-center gap-3 p-0 text-left min-w-0 grow">
<div class="size-6 shrink-0">
- <Avatar fallback={name()} background="var(--surface-info-base)" class="size-full" />
+ <Avatar
+ fallback={name()}
+ src={props.project.icon?.url}
+ background={props.project.icon?.color ?? "var(--surface-info-base)"}
+ class="size-full"
+ />
</div>
<span class="truncate text-14-medium text-text-strong">{name()}</span>
</div>
@@ -164,11 +169,16 @@ export default function Layout(props: ParentProps) {
variant="ghost"
size="large"
class="flex items-center justify-center p-0 aspect-square border-none"
- data-selected={props.directory === currentDirectory()}
- onClick={() => navigateToProject(props.directory)}
+ data-selected={props.project.worktree === currentDirectory()}
+ onClick={() => navigateToProject(props.project.worktree)}
>
<div class="size-6 shrink-0">
- <Avatar fallback={name()} background="var(--surface-info-base)" class="size-full" />
+ <Avatar
+ fallback={name()}
+ src={props.project.icon?.url}
+ background={props.project.icon?.color ?? "var(--surface-info-base)"}
+ class="size-full"
+ />
</div>
</Button>
</Match>
@@ -194,18 +204,12 @@ export default function Layout(props: ParentProps) {
>
<Collapsible.Trigger class="group/trigger flex items-center gap-3 p-0 text-left min-w-0 grow border-none">
<div class="size-6 shrink-0">
- <Switch>
- <Match when={props.project.icon?.url}>
- {(url) => <img src={url()} class="size-full group-hover/session:hidden" />}
- </Match>
- <Match when={true}>
- <Avatar
- fallback={name()}
- background={props.project.icon?.color ?? "var(--surface-info-base)"}
- class="size-full group-hover/session:hidden"
- />
- </Match>
- </Switch>
+ <Avatar
+ fallback={name()}
+ src={props.project.icon?.url}
+ background={props.project.icon?.color ?? "var(--surface-info-base)"}
+ class="size-full group-hover/session:hidden"
+ />
<Icon
name="chevron-right"
size="large"
@@ -282,7 +286,7 @@ export default function Layout(props: ParentProps) {
</Match>
<Match when={true}>
<Tooltip placement="right" value={props.project.worktree}>
- <ProjectVisual directory={props.project.worktree} />
+ <ProjectVisual project={props.project} />
</Tooltip>
</Match>
</Switch>
@@ -291,11 +295,12 @@ export default function Layout(props: ParentProps) {
}
const ProjectDragOverlay = (): JSX.Element => {
+ const project = createMemo(() => layout.projects.list().find((p) => p.worktree === store.activeDraggable))
return (
- <Show when={store.activeDraggable}>
- {(directory) => (
+ <Show when={project()}>
+ {(p) => (
<div class="bg-background-base rounded-md">
- <ProjectVisual directory={directory()} />
+ <ProjectVisual project={p()} />
</div>
)}
</Show>
diff --git a/packages/ui/src/components/avatar.css b/packages/ui/src/components/avatar.css
index bc87f3bd8..4e42e6f99 100644
--- a/packages/ui/src/components/avatar.css
+++ b/packages/ui/src/components/avatar.css
@@ -13,6 +13,11 @@
color: oklch(from var(--avatar-bg) calc(l * 0.72) calc(c * 8) h);
}
+[data-component="avatar"][data-has-image] {
+ background-color: transparent;
+ border: none;
+}
+
[data-component="avatar"][data-size="small"] {
width: 1.25rem;
height: 1.25rem;
diff --git a/packages/ui/src/components/avatar.tsx b/packages/ui/src/components/avatar.tsx
index 183a15b9b..1ff3008ee 100644
--- a/packages/ui/src/components/avatar.tsx
+++ b/packages/ui/src/components/avatar.tsx
@@ -2,27 +2,31 @@ import { type ComponentProps, splitProps, Show } from "solid-js"
export interface AvatarProps extends ComponentProps<"div"> {
fallback: string
+ src?: string
background?: string
size?: "small" | "normal" | "large"
}
export function Avatar(props: AvatarProps) {
- const [split, rest] = splitProps(props, ["fallback", "background", "size", "class", "classList", "style"])
+ const [split, rest] = splitProps(props, ["fallback", "src", "background", "size", "class", "classList", "style"])
return (
<div
{...rest}
data-component="avatar"
data-size={split.size || "normal"}
+ data-has-image={split.src ? "" : undefined}
classList={{
...(split.classList ?? {}),
[split.class ?? ""]: !!split.class,
}}
style={{
...(typeof split.style === "object" ? split.style : {}),
- ...(split.background ? { "--avatar-bg": split.background } : {}),
+ ...(!split.src && split.background ? { "--avatar-bg": split.background } : {}),
}}
>
- <Show when={split.fallback}>{split.fallback[0]}</Show>
+ <Show when={split.src} fallback={split.fallback?.[0]}>
+ {(src) => <img src={src()} draggable={false} class="size-full object-cover" />}
+ </Show>
</div>
)
}