mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-22 02:04:22 +01:00
wip: desktop work
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
DiffChanges,
|
||||
ProgressCircle,
|
||||
Message,
|
||||
Typewriter,
|
||||
} from "@opencode-ai/ui"
|
||||
import { FileIcon } from "@/ui"
|
||||
import FileTree from "@/components/file-tree"
|
||||
@@ -544,7 +545,6 @@ export default function Page() {
|
||||
<For each={local.session.userMessages()}>
|
||||
{(message) => {
|
||||
const diffs = createMemo(() => message.summary?.diffs ?? [])
|
||||
|
||||
return (
|
||||
<li
|
||||
class="group/li flex items-center gap-x-2 py-1 self-stretch cursor-default"
|
||||
@@ -570,9 +570,9 @@ export default function Page() {
|
||||
<div class="flex flex-col items-start gap-50 pb-50">
|
||||
<For each={local.session.userMessages()}>
|
||||
{(message) => {
|
||||
const [initialized, setInitialized] = createSignal(!!message.summary?.title)
|
||||
const [expanded, setExpanded] = createSignal(false)
|
||||
const parts = createMemo(() => sync.data.part[message.id])
|
||||
const prompt = createMemo(() => local.session.getMessageText(message))
|
||||
const title = createMemo(() => message.summary?.title)
|
||||
const summary = createMemo(() => message.summary?.body)
|
||||
const assistantMessages = createMemo(() => {
|
||||
@@ -581,6 +581,9 @@ export default function Page() {
|
||||
) as AssistantMessageType[]
|
||||
})
|
||||
const working = createMemo(() => !summary())
|
||||
createEffect(() => {
|
||||
setTimeout(() => setInitialized(!!title()), 10_000)
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -589,9 +592,11 @@ export default function Page() {
|
||||
>
|
||||
{/* Title */}
|
||||
<div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10">
|
||||
<h1 class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
|
||||
{title() ?? prompt()}
|
||||
</h1>
|
||||
<div class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
|
||||
<Show when={initialized()} fallback={<Typewriter as="h1" text={title()} />}>
|
||||
<h1>{title()}</h1>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={title}>
|
||||
<div class="-mt-8">
|
||||
|
||||
@@ -20,6 +20,7 @@ export * from "./select-dialog"
|
||||
export * from "./tabs"
|
||||
export * from "./basic-tool"
|
||||
export * from "./tooltip"
|
||||
export * from "./typewriter"
|
||||
|
||||
export * from "../context/helper"
|
||||
export * from "../context/shiki"
|
||||
|
||||
14
packages/ui/src/components/typewriter.css
Normal file
14
packages/ui/src/components/typewriter.css
Normal file
@@ -0,0 +1,14 @@
|
||||
@keyframes blink {
|
||||
0%,
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
51%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.blinking-cursor {
|
||||
animation: blink 1s step-end infinite;
|
||||
}
|
||||
54
packages/ui/src/components/typewriter.tsx
Normal file
54
packages/ui/src/components/typewriter.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { createEffect, Show, type ValidComponent } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Dynamic } from "solid-js/web"
|
||||
|
||||
export const Typewriter = <T extends ValidComponent = "p">(props: {
|
||||
text?: string
|
||||
class?: string
|
||||
as?: T
|
||||
}) => {
|
||||
const [store, setStore] = createStore({
|
||||
typing: false,
|
||||
displayed: "",
|
||||
cursor: true,
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const text = props.text
|
||||
if (!text) return
|
||||
|
||||
let i = 0
|
||||
setStore("typing", true)
|
||||
setStore("displayed", "")
|
||||
setStore("cursor", true)
|
||||
|
||||
const getTypingDelay = () => {
|
||||
const random = Math.random()
|
||||
if (random < 0.05) return 150 + Math.random() * 100
|
||||
if (random < 0.15) return 80 + Math.random() * 60
|
||||
return 30 + Math.random() * 50
|
||||
}
|
||||
|
||||
const type = () => {
|
||||
if (i < text.length) {
|
||||
setStore("displayed", text.slice(0, i + 1))
|
||||
i++
|
||||
setTimeout(type, getTypingDelay())
|
||||
} else {
|
||||
setStore("typing", false)
|
||||
setTimeout(() => setStore("cursor", false), 2000)
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(type, 200)
|
||||
})
|
||||
|
||||
return (
|
||||
<Dynamic component={props.as || "p"} class={props.class}>
|
||||
{store.displayed}
|
||||
<Show when={store.cursor}>
|
||||
<span classList={{ "blinking-cursor": !store.typing }}>│</span>
|
||||
</Show>
|
||||
</Dynamic>
|
||||
)
|
||||
}
|
||||
@@ -25,5 +25,6 @@
|
||||
@import "../components/select-dialog.css" layer(components);
|
||||
@import "../components/tabs.css" layer(components);
|
||||
@import "../components/tooltip.css" layer(components);
|
||||
@import "../components/typewriter.css" layer(components);
|
||||
|
||||
@import "./utilities.css" layer(utilities);
|
||||
|
||||
Reference in New Issue
Block a user