Просмотр исходного кода

fix(desktop): content animations

Adam 2 месяцев назад
Родитель
Сommit
333948711d

+ 93 - 1
packages/ui/src/components/session-turn.css

@@ -101,7 +101,99 @@
     }
 
     &[data-fade="true"] > * {
-      animation: fade-up-text 0.3s ease-out forwards;
+      animation: fadeUp 0.4s ease-out forwards;
+      opacity: 0;
+
+      &:nth-child(1) {
+        animation-delay: 0.1s;
+      }
+      &:nth-child(2) {
+        animation-delay: 0.2s;
+      }
+      &:nth-child(3) {
+        animation-delay: 0.3s;
+      }
+      &:nth-child(4) {
+        animation-delay: 0.4s;
+      }
+      &:nth-child(5) {
+        animation-delay: 0.5s;
+      }
+      &:nth-child(6) {
+        animation-delay: 0.6s;
+      }
+      &:nth-child(7) {
+        animation-delay: 0.7s;
+      }
+      &:nth-child(8) {
+        animation-delay: 0.8s;
+      }
+      &:nth-child(9) {
+        animation-delay: 0.9s;
+      }
+      &:nth-child(10) {
+        animation-delay: 1s;
+      }
+      &:nth-child(11) {
+        animation-delay: 1.1s;
+      }
+      &:nth-child(12) {
+        animation-delay: 1.2s;
+      }
+      &:nth-child(13) {
+        animation-delay: 1.3s;
+      }
+      &:nth-child(14) {
+        animation-delay: 1.4s;
+      }
+      &:nth-child(15) {
+        animation-delay: 1.5s;
+      }
+      &:nth-child(16) {
+        animation-delay: 1.6s;
+      }
+      &:nth-child(17) {
+        animation-delay: 1.7s;
+      }
+      &:nth-child(18) {
+        animation-delay: 1.8s;
+      }
+      &:nth-child(19) {
+        animation-delay: 1.9s;
+      }
+      &:nth-child(20) {
+        animation-delay: 2s;
+      }
+      &:nth-child(21) {
+        animation-delay: 2.1s;
+      }
+      &:nth-child(22) {
+        animation-delay: 2.2s;
+      }
+      &:nth-child(23) {
+        animation-delay: 2.3s;
+      }
+      &:nth-child(24) {
+        animation-delay: 2.4s;
+      }
+      &:nth-child(25) {
+        animation-delay: 2.5s;
+      }
+      &:nth-child(26) {
+        animation-delay: 2.6s;
+      }
+      &:nth-child(27) {
+        animation-delay: 2.7s;
+      }
+      &:nth-child(28) {
+        animation-delay: 2.8s;
+      }
+      &:nth-child(29) {
+        animation-delay: 2.9s;
+      }
+      &:nth-child(30) {
+        animation-delay: 3s;
+      }
     }
   }
 

+ 25 - 12
packages/ui/src/components/session-turn.tsx

@@ -2,7 +2,7 @@ import { AssistantMessage } from "@opencode-ai/sdk"
 import { useData } from "../context"
 import { Binary } from "@opencode-ai/util/binary"
 import { getDirectory, getFilename } from "@opencode-ai/util/path"
-import { createEffect, createMemo, createSignal, For, Match, ParentProps, Show, Switch } from "solid-js"
+import { createEffect, createMemo, createSignal, For, Match, onMount, ParentProps, Show, Switch } from "solid-js"
 import { DiffChanges } from "./diff-changes"
 import { Typewriter } from "./typewriter"
 import { Message } from "./message-part"
@@ -55,10 +55,11 @@ export function SessionTurn(
       <div data-slot="session-turn-content" class={props.classes?.content}>
         <Show when={message()}>
           {(msg) => {
-            const titleSeen = createMemo(() => true)
-            const contentSeen = createMemo(() => true)
+            const titleKey = `app:seen:session:${props.sessionID}:${msg().id}:title`
+            const contentKey = `app:seen:session:${props.sessionID}:${msg().id}:content`
             const [detailsExpanded, setDetailsExpanded] = createSignal(false)
-            const [titled, setTitled] = createSignal(titleSeen())
+            const [titled, setTitled] = createSignal(true)
+            const [faded, setFaded] = createSignal(true)
 
             const assistantMessages = createMemo(() => {
               return messages()?.filter((m) => m.role === "assistant" && m.parentID == msg().id) as AssistantMessage[]
@@ -68,7 +69,7 @@ export function SessionTurn(
             const parts = createMemo(() => data.part[msg().id])
             const lastTextPart = createMemo(() =>
               assistantMessageParts()
-                .filter((p) => p.type === "text")
+                .filter((p) => p?.type === "text")
                 ?.at(-1),
             )
             const hasToolPart = createMemo(() => assistantMessageParts().some((p) => p?.type === "tool"))
@@ -79,11 +80,23 @@ export function SessionTurn(
             const lastTextPartShown = createMemo(() => !msg().summary?.body && (lastTextPart()?.text?.length ?? 0) > 0)
 
             // allowing time for the animations to finish
-            createEffect(() => {
-              if (titleSeen()) return
-              const title = msg().summary?.title
-              if (title) setTimeout(() => setTitled(true), 10_000)
+            onMount(() => {
+              const titleSeen = sessionStorage.getItem(titleKey) === "true"
+              const contentSeen = sessionStorage.getItem(contentKey) === "true"
+
+              if (!titleSeen) {
+                setTitled(false)
+                const title = msg().summary?.title
+                if (title) setTimeout(() => setTitled(true), 10_000)
+                setTimeout(() => sessionStorage.setItem(titleKey, "true"), 1000)
+              }
+
+              if (!contentSeen) {
+                setFaded(false)
+                setTimeout(() => sessionStorage.setItem(contentKey, "true"), 1000)
+              }
             })
+
             createEffect(() => {
               const completed = !messageWorking()
               setTimeout(() => setCompleted(completed), 1200)
@@ -120,7 +133,7 @@ export function SessionTurn(
                           <Markdown
                             data-slot="session-turn-markdown"
                             data-diffs={!!msg().summary?.diffs?.length}
-                            data-fade={!msg().summary?.diffs?.length && !contentSeen()}
+                            data-fade={!msg().summary?.diffs?.length && !faded()}
                             text={summary()}
                           />
                         )}
@@ -201,14 +214,14 @@ export function SessionTurn(
                                 const parts = createMemo(() => data.part[assistantMessage.id])
                                 const last = createMemo(() =>
                                   parts()
-                                    .filter((p) => p.type === "text")
+                                    .filter((p) => p?.type === "text")
                                     .at(-1),
                                 )
                                 if (lastTextPartShown() && lastTextPart()?.id === last()?.id) {
                                   return (
                                     <Message
                                       message={assistantMessage}
-                                      parts={parts().filter((p) => p.id !== last()?.id)}
+                                      parts={parts().filter((p) => p?.id !== last()?.id)}
                                     />
                                   )
                                 }

+ 0 - 20
packages/ui/src/hooks/create-seen.ts

@@ -1,20 +0,0 @@
-import { createSignal, onCleanup, onMount } from "solid-js"
-import { isServer } from "solid-js/web"
-
-export function createSeen(key: string, delay = 1000) {
-  // 1. Initialize state based on storage (default to true on server to avoid flash)
-  const storageKey = `app:seen:${key}`
-  const [hasSeen] = createSignal(!isServer && sessionStorage.getItem(storageKey) === "true")
-
-  onMount(() => {
-    // 2. If we haven't seen it, mark it as seen for NEXT time
-    if (!hasSeen()) {
-      const timer = setTimeout(() => {
-        sessionStorage.setItem(storageKey, "true")
-      }, delay)
-      onCleanup(() => clearTimeout(timer))
-    }
-  })
-
-  return hasSeen
-}

+ 0 - 1
packages/ui/src/hooks/index.ts

@@ -1,2 +1 @@
 export * from "./use-filtered-list"
-export * from "./create-seen"