Răsfoiți Sursa

wip(desktop): progress

Adam 2 luni în urmă
părinte
comite
1fbd7a7f9a

+ 27 - 22
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>

+ 5 - 0
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;

+ 7 - 3
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>
   )
 }