mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 09:44:21 +01:00
wip: desktop work
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { For, JSXElement, Match, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
import { For, JSXElement, Match, Show, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||||
import { Part } from "@opencode-ai/ui"
|
import { Markdown, Part } from "@opencode-ai/ui"
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import type { AssistantMessage as AssistantMessageType, Part as PartType, ToolPart } from "@opencode-ai/sdk"
|
import type { AssistantMessage as AssistantMessageType, Part as PartType, ToolPart } from "@opencode-ai/sdk"
|
||||||
|
import { Spinner } from "./spinner"
|
||||||
|
|
||||||
export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) {
|
export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) {
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
@@ -22,37 +23,42 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||||||
) as ToolPart,
|
) as ToolPart,
|
||||||
)
|
)
|
||||||
|
|
||||||
const eligibleItems = createMemo(() => {
|
const resolvedParts = createMemo(() => {
|
||||||
let allParts = parts()
|
let resolved = parts()
|
||||||
const task = currentTask()
|
const task = currentTask()
|
||||||
if (task && task.state && "metadata" in task.state && task.state.metadata?.sessionId) {
|
if (task && task.state && "metadata" in task.state && task.state.metadata?.sessionId) {
|
||||||
const messages = sync.data.message[task.state.metadata.sessionId as string]?.filter((m) => m.role === "assistant")
|
const messages = sync.data.message[task.state.metadata.sessionId as string]?.filter((m) => m.role === "assistant")
|
||||||
allParts = messages?.flatMap((m) => sync.data.part[m.id]) ?? parts()
|
resolved = messages?.flatMap((m) => sync.data.part[m.id]) ?? parts()
|
||||||
}
|
}
|
||||||
return allParts.filter(
|
return resolved
|
||||||
(p) =>
|
})
|
||||||
p?.type === "text" ||
|
const currentText = createMemo(
|
||||||
(p?.type === "reasoning" && p.time?.end) ||
|
() =>
|
||||||
(p?.type === "tool" && p.state.status === "completed"),
|
resolvedParts().findLast((p) => p?.type === "text")?.text ||
|
||||||
|
resolvedParts().findLast((p) => p?.type === "reasoning")?.text,
|
||||||
)
|
)
|
||||||
|
const eligibleItems = createMemo(() => {
|
||||||
|
return resolvedParts().filter((p) => p?.type === "tool" && p.state.status === "completed")
|
||||||
})
|
})
|
||||||
const finishedItems = createMemo<(JSXElement | PartType)[]>(() => [
|
const finishedItems = createMemo<(JSXElement | PartType)[]>(() => [
|
||||||
"",
|
<div class="h-8 w-full" />,
|
||||||
"",
|
<div class="h-8 w-full" />,
|
||||||
<div class="text-text-diff-add-base">Loading...</div>,
|
<div class="flex items-center gap-x-5 pl-3 text-text-base">
|
||||||
|
<Spinner /> <span class="text-12-medium">Thinking...</span>
|
||||||
|
</div>,
|
||||||
...eligibleItems(),
|
...eligibleItems(),
|
||||||
...(done() ? ["", "", ""] : []),
|
...(done() ? [<div class="h-8 w-full" />, <div class="h-8 w-full" />, <div class="h-8 w-full" />] : []),
|
||||||
])
|
])
|
||||||
|
|
||||||
const MINIMUM_DELAY = 400
|
const delay = createMemo(() => (done() ? 220 : 400))
|
||||||
const [visibleCount, setVisibleCount] = createSignal(1)
|
const [visibleCount, setVisibleCount] = createSignal(eligibleItems().length)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const total = finishedItems().length
|
const total = finishedItems().length
|
||||||
if (total > visibleCount()) {
|
if (total > visibleCount()) {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
setVisibleCount((prev) => prev + 1)
|
setVisibleCount((prev) => prev + 1)
|
||||||
}, MINIMUM_DELAY)
|
}, delay())
|
||||||
onCleanup(() => clearTimeout(timer))
|
onCleanup(() => clearTimeout(timer))
|
||||||
} else if (total < visibleCount()) {
|
} else if (total < visibleCount()) {
|
||||||
setVisibleCount(total)
|
setVisibleCount(total)
|
||||||
@@ -66,10 +72,11 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
<div
|
<div
|
||||||
class="h-30 overflow-hidden pointer-events-none
|
class="h-30 overflow-hidden pointer-events-none pb-1
|
||||||
mask-alpha mask-t-from-33% mask-t-from-background-base mask-t-to-transparent
|
mask-alpha mask-t-from-33% mask-t-from-background-base mask-t-to-transparent
|
||||||
mask-b-from-90% mask-b-from-background-base mask-b-to-transparent"
|
mask-b-from-95% mask-b-from-background-base mask-b-to-transparent"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full flex flex-col items-start self-stretch gap-2 py-8
|
class="w-full flex flex-col items-start self-stretch gap-2 py-8
|
||||||
@@ -94,7 +101,9 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||||||
<Match when={part.type === "reasoning" && part}>
|
<Match when={part.type === "reasoning" && part}>
|
||||||
{(p) => <Part message={message()!} part={p()} />}
|
{(p) => <Part message={message()!} part={p()} />}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={part.type === "tool" && part}>{(p) => <Part message={message()!} part={p()} />}</Match>
|
<Match when={part.type === "tool" && part}>
|
||||||
|
{(p) => <Part message={message()!} part={p()} />}
|
||||||
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -104,5 +113,16 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={currentText()}>
|
||||||
|
{(text) => (
|
||||||
|
<div
|
||||||
|
class="max-h-36 flex flex-col justify-end overflow-hidden py-3
|
||||||
|
mask-alpha mask-t-from-80% mask-t-from-background-base mask-t-to-transparent"
|
||||||
|
>
|
||||||
|
<Markdown text={text()} class="w-full shrink-0 overflow-visible" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
classList={{
|
classList={{
|
||||||
"bg-surface-raised-stronger-non-alpha border border-border-strong-base": true,
|
"bg-surface-raised-stronger-non-alpha border border-border-strong-base": true,
|
||||||
"rounded-2xl overflow-clip focus-within:shadow-xs-border-selected": true,
|
"rounded-2xl overflow-clip focus-within:border-transparent focus-within:shadow-xs-border-select": true,
|
||||||
[props.class ?? ""]: !!props.class,
|
[props.class ?? ""]: !!props.class,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -548,7 +548,79 @@ export default function Page() {
|
|||||||
<For each={local.session.userMessages()}>
|
<For each={local.session.userMessages()}>
|
||||||
{(message) => {
|
{(message) => {
|
||||||
const diffs = createMemo(() => message.summary?.diffs ?? [])
|
const diffs = createMemo(() => message.summary?.diffs ?? [])
|
||||||
const working = createMemo(() => !message.summary?.title)
|
const working = createMemo(() => !message.summary?.body)
|
||||||
|
const assistantMessages = createMemo(() => {
|
||||||
|
return sync.data.message[activeSession().id]?.filter(
|
||||||
|
(m) => m.role === "assistant" && m.parentID == message.id,
|
||||||
|
) as AssistantMessageType[]
|
||||||
|
})
|
||||||
|
const parts = createMemo(() =>
|
||||||
|
assistantMessages().flatMap((m) => sync.data.part[m.id]),
|
||||||
|
)
|
||||||
|
const lastPart = createMemo(() => parts().slice(-1)?.at(0))
|
||||||
|
const rawStatus = createMemo(() => {
|
||||||
|
const defaultStatus = "Working..."
|
||||||
|
const last = lastPart()
|
||||||
|
if (!last) return defaultStatus
|
||||||
|
|
||||||
|
if (last.type === "tool") {
|
||||||
|
switch (last.tool) {
|
||||||
|
case "task":
|
||||||
|
return "Delegating work..."
|
||||||
|
case "todowrite":
|
||||||
|
case "todoread":
|
||||||
|
return "Planning next steps..."
|
||||||
|
case "read":
|
||||||
|
return "Gathering context..."
|
||||||
|
case "list":
|
||||||
|
case "grep":
|
||||||
|
case "glob":
|
||||||
|
return "Searching the codebase..."
|
||||||
|
case "webfetch":
|
||||||
|
return "Searching the web..."
|
||||||
|
case "edit":
|
||||||
|
case "write":
|
||||||
|
return "Making edits..."
|
||||||
|
case "bash":
|
||||||
|
return "Running commands..."
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if (last.type === "reasoning") {
|
||||||
|
return "Thinking..."
|
||||||
|
} else if (last.type === "text") {
|
||||||
|
return "Gathering thoughts..."
|
||||||
|
}
|
||||||
|
return defaultStatus
|
||||||
|
})
|
||||||
|
|
||||||
|
const [status, setStatus] = createSignal(rawStatus())
|
||||||
|
let lastStatusChange = Date.now()
|
||||||
|
let statusTimeout: number | undefined
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const newStatus = rawStatus()
|
||||||
|
if (newStatus === status()) return
|
||||||
|
|
||||||
|
const timeSinceLastChange = Date.now() - lastStatusChange
|
||||||
|
|
||||||
|
if (timeSinceLastChange >= 1000) {
|
||||||
|
setStatus(newStatus)
|
||||||
|
lastStatusChange = Date.now()
|
||||||
|
if (statusTimeout) {
|
||||||
|
clearTimeout(statusTimeout)
|
||||||
|
statusTimeout = undefined
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (statusTimeout) clearTimeout(statusTimeout)
|
||||||
|
statusTimeout = setTimeout(() => {
|
||||||
|
setStatus(rawStatus())
|
||||||
|
lastStatusChange = Date.now()
|
||||||
|
statusTimeout = undefined
|
||||||
|
}, 1000 - timeSinceLastChange) as unknown as number
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li class="group/li flex items-center self-stretch">
|
<li class="group/li flex items-center self-stretch">
|
||||||
<button
|
<button
|
||||||
@@ -570,7 +642,10 @@ export default function Page() {
|
|||||||
"text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base": true,
|
"text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base": true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{message.summary?.title ?? local.session.getMessageText(message)}
|
<Switch>
|
||||||
|
<Match when={working()}>{status()}</Match>
|
||||||
|
<Match when={true}>{message.summary?.title}</Match>
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@@ -604,10 +679,12 @@ export default function Page() {
|
|||||||
|
|
||||||
// allowing time for the animations to finish
|
// allowing time for the animations to finish
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
title()
|
||||||
setTimeout(() => setTitled(!!title()), 10_000)
|
setTimeout(() => setTitled(!!title()), 10_000)
|
||||||
})
|
})
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
setTimeout(() => setCompleted(!!summary()), 3_000)
|
summary()
|
||||||
|
setTimeout(() => setCompleted(!!summary()), 1200)
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -618,9 +695,18 @@ export default function Page() {
|
|||||||
>
|
>
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10">
|
<div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10">
|
||||||
<div class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
|
<div class="w-full text-14-medium text-text-strong">
|
||||||
<Show when={titled()} fallback={<Typewriter as="h1" text={title()} />}>
|
<Show
|
||||||
<h1>{title()}</h1>
|
when={titled()}
|
||||||
|
fallback={
|
||||||
|
<Typewriter
|
||||||
|
as="h1"
|
||||||
|
text={title()}
|
||||||
|
class="overflow-hidden text-ellipsis min-w-0 text-nowrap"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<h1 class="overflow-hidden text-ellipsis min-w-0 text-nowrap">{title()}</h1>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -628,7 +714,7 @@ export default function Page() {
|
|||||||
<Message message={message} parts={parts()} />
|
<Message message={message} parts={parts()} />
|
||||||
</div>
|
</div>
|
||||||
{/* Summary */}
|
{/* Summary */}
|
||||||
<Show when={!working()}>
|
<Show when={completed()}>
|
||||||
<div class="w-full flex flex-col gap-6 items-start self-stretch">
|
<div class="w-full flex flex-col gap-6 items-start self-stretch">
|
||||||
<div class="flex flex-col items-start gap-1 self-stretch">
|
<div class="flex flex-col items-start gap-1 self-stretch">
|
||||||
<h2 class="text-12-medium text-text-weak">
|
<h2 class="text-12-medium text-text-weak">
|
||||||
@@ -637,7 +723,9 @@ export default function Page() {
|
|||||||
<Match when={true}>Response</Match>
|
<Match when={true}>Response</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</h2>
|
</h2>
|
||||||
<Show when={summary()}>{(summary) => <Markdown text={summary()} />}</Show>
|
<Show when={summary()}>
|
||||||
|
{(summary) => <Markdown class="[&>*]:fade-up-text" text={summary()} />}
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<Accordion class="w-full" multiple>
|
<Accordion class="w-full" multiple>
|
||||||
<For each={diffs()}>
|
<For each={diffs()}>
|
||||||
@@ -699,8 +787,8 @@ export default function Page() {
|
|||||||
<div class="flex items-center gap-1 self-stretch">
|
<div class="flex items-center gap-1 self-stretch">
|
||||||
<div class="text-12-medium">
|
<div class="text-12-medium">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={expanded()}>Hide steps</Match>
|
<Match when={expanded()}>Hide details</Match>
|
||||||
<Match when={!expanded()}>Show steps</Match>
|
<Match when={!expanded()}>Show details</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<Collapsible.Arrow />
|
<Collapsible.Arrow />
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
color: var(--text-base);
|
color: var(--text-base);
|
||||||
text-wrap: pretty;
|
text-wrap: pretty;
|
||||||
|
|
||||||
/* text-14-regular */
|
/* text-12-regular */
|
||||||
font-family: var(--font-family-sans);
|
font-family: var(--font-family-sans);
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-small);
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: var(--font-weight-regular);
|
font-weight: var(--font-weight-regular);
|
||||||
line-height: var(--line-height-large); /* 142.857% */
|
line-height: var(--line-height-large); /* 166.667% */
|
||||||
letter-spacing: var(--letter-spacing-normal);
|
letter-spacing: var(--letter-spacing-normal);
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
|
|||||||
@@ -11,3 +11,110 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fadeUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(5px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-up-text {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
|
|
||||||
--shadow-xs: var(--shadow-xs);
|
--shadow-xs: var(--shadow-xs);
|
||||||
--shadow-md: var(--shadow-md);
|
--shadow-md: var(--shadow-md);
|
||||||
--shadow-xs-border-selected: var(--shadow-xs-border-selected);
|
--shadow-xs-border-select: var(--shadow-xs-border-select);
|
||||||
|
|
||||||
--animate-pulse: var(--animate-pulse);
|
--animate-pulse: var(--animate-pulse);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,99 @@
|
|||||||
direction: rtl;
|
direction: rtl;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@utility fade-up-text {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user