mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
fix(desktop): content animations
This commit is contained in:
parent
001b4be0ae
commit
333948711d
4 changed files with 118 additions and 34 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,2 +1 @@
|
|||
export * from "./use-filtered-list"
|
||||
export * from "./create-seen"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue