Selaa lähdekoodia

feat(app): new update button (#30460)

Co-authored-by: Brendan Allan <git@brendonovich.dev>
Aarav Sareen 3 kuukautta sitten
vanhempi
commit
a444410cd4
2 muutettua tiedostoa jossa 52 lisäystä ja 14 poistoa
  1. 26 13
      packages/app/src/components/titlebar.tsx
  2. 26 1
      packages/app/src/index.css

+ 26 - 13
packages/app/src/components/titlebar.tsx

@@ -121,10 +121,11 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
   const hasProjects = createMemo(() => layout.projects.list().length > 0)
   const nav = createMemo(() => (useV2Titlebar() ? settings.general.showNavigation() : true))
   const updateState = createMemo<TitlebarUpdatePillState>(() => {
+    const installing = props.update?.installing() ?? false
     const version = props.update?.version()
     return {
-      visible: version !== undefined,
-      installing: props.update?.installing() ?? false,
+      visible: version !== undefined || installing,
+      installing,
       label: "Update",
       ariaLabel: language.t("toast.update.action.installRestart"),
       title: version ? `Update ${version}` : undefined,
@@ -219,9 +220,9 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
   return (
     <header
       classList={{
-        "shrink-0 relative overflow-hidden flex flex-row": true,
-        "h-9 bg-v2-background-bg-deep": useV2Titlebar(),
-        "h-10 bg-background-base": !useV2Titlebar(),
+        "shrink-0 relative flex flex-row": true,
+        "h-9 bg-v2-background-bg-deep overflow-visible": useV2Titlebar(),
+        "h-10 bg-background-base overflow-hidden": !useV2Titlebar(),
       }}
       style={{
         "min-height": minHeight(),
@@ -705,27 +706,39 @@ type TitlebarV2RightState = {
 
 function TitlebarV2Right(props: { state: TitlebarV2RightState }) {
   return (
-    <div class="flex shrink-0 items-center justify-end gap-0">
-      <TitlebarUpdatePill state={props.state.update} />
+    <div class="relative z-20 flex shrink-0 items-center justify-end gap-0 overflow-visible">
+      <TitlebarUpdateIconButton state={props.state.update} />
       <div id="opencode-titlebar-right" class="flex shrink-0 items-center justify-end gap-0" />
     </div>
   )
 }
 
-function TitlebarUpdatePill(props: { state: TitlebarUpdatePillState }) {
+function TitlebarUpdateIconButton(props: { state: TitlebarUpdatePillState }) {
   return (
-    <Show when={props.state.visible}>
+    <div class="relative isolate mr-3 size-5 shrink-0">
       <button
         type="button"
-        class="h-5 shrink-0 rounded-[27px] bg-[var(--v2-background-bg-layer-03)] px-2.5 text-[11px] font-[530] leading-4 tracking-[0.05px] text-[var(--v2-text-text-base)] disabled:opacity-60"
+        class="group absolute right-0 top-0 z-10 flex h-5 w-5 items-center justify-end overflow-hidden rounded-full bg-v2-icon-icon-accent/20 text-v2-icon-icon-accent transition-[width,background-color] duration-150 ease-out hover:z-30 hover:w-[68px] hover:bg-[color-mix(in_srgb,var(--v2-icon-icon-accent)_20%,var(--v2-background-bg-deep))] focus-visible:z-30 focus-visible:w-[68px] focus-visible:bg-[color-mix(in_srgb,var(--v2-icon-icon-accent)_20%,var(--v2-background-bg-deep))] focus-visible:outline-none disabled:opacity-60 motion-reduce:transition-none"
         onClick={props.state.onInstall}
         disabled={props.state.installing}
+        aria-busy={props.state.installing}
         aria-label={props.state.ariaLabel}
-        title={props.state.title}
       >
-        {props.state.label}
+        <span class="shrink-0 ml-[8px] mr-px text-[11px] text-v2-text-text-accent [font-weight:530] opacity-0 translate-x-2 motion-safe:transition-all duration-150 ease-out group-hover:opacity-100 group-hover:translate-x-0 group-focus-visible:opacity-100 group-focus-visible:translate-x-0 motion-reduce:translate-x-0">
+          Update
+        </span>
+        <span class="flex size-5 shrink-0 items-center justify-center">
+          <Show
+            when={!props.state.installing}
+            fallback={<span data-slot="titlebar-update-loader" aria-hidden="true" />}
+          >
+            <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">
+              <path d="M7 11V3M3.5 7.63128L7 11L10.5 7.63128" stroke="currentColor" />
+            </svg>
+          </Show>
+        </span>
       </button>
-    </Show>
+    </div>
   )
 }
 

+ 26 - 1
packages/app/src/index.css

@@ -11,7 +11,7 @@
 @font-face {
   font-family: "Inter";
   src: url("/assets/Inter.ttf") format("truetype");
-  font-weight: normal;
+  font-weight: 100 900;
   font-style: normal;
 }
 
@@ -137,6 +137,31 @@
     }
   }
 
+  [data-slot="titlebar-update-loader"] {
+    display: block;
+    flex-shrink: 0;
+    margin: 1px;
+    width: 12px;
+    height: 12px;
+    border-radius: 9999px;
+    border: 2px solid color-mix(in srgb, var(--v2-icon-icon-accent) 30%, transparent);
+    border-top-color: var(--v2-icon-icon-accent);
+    animation: titlebar-update-loader-spin 0.67s linear infinite;
+    transform-origin: 50% 50%;
+  }
+
+  @media (prefers-reduced-motion: reduce) {
+    [data-slot="titlebar-update-loader"] {
+      animation: none;
+    }
+  }
+
+  @keyframes titlebar-update-loader-spin {
+    to {
+      transform: rotate(360deg);
+    }
+  }
+
   @keyframes fade-in {
     from {
       opacity: 0;