diff options
| author | Adam <[email protected]> | 2025-10-31 09:45:57 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-31 09:45:57 -0500 |
| commit | 0ac943de909fb5a685a608d7894248cdd3a9e129 (patch) | |
| tree | 6d2e0c0cb1a44de4f53e74a7002622842dab7591 /packages/ui/src | |
| parent | 485135cf5c4af64d1449aa3cadcdd0aef92201a3 (diff) | |
| download | opencode-0ac943de909fb5a685a608d7894248cdd3a9e129.tar.gz opencode-0ac943de909fb5a685a608d7894248cdd3a9e129.zip | |
wip: desktop work
Diffstat (limited to 'packages/ui/src')
| -rw-r--r-- | packages/ui/src/components/index.ts | 1 | ||||
| -rw-r--r-- | packages/ui/src/components/typewriter.css | 14 | ||||
| -rw-r--r-- | packages/ui/src/components/typewriter.tsx | 54 | ||||
| -rw-r--r-- | packages/ui/src/styles/index.css | 1 |
4 files changed, 70 insertions, 0 deletions
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 115e5f14f..499f2e27f 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -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" diff --git a/packages/ui/src/components/typewriter.css b/packages/ui/src/components/typewriter.css new file mode 100644 index 000000000..e978312a9 --- /dev/null +++ b/packages/ui/src/components/typewriter.css @@ -0,0 +1,14 @@ +@keyframes blink { + 0%, + 50% { + opacity: 1; + } + 51%, + 100% { + opacity: 0; + } +} + +.blinking-cursor { + animation: blink 1s step-end infinite; +} diff --git a/packages/ui/src/components/typewriter.tsx b/packages/ui/src/components/typewriter.tsx new file mode 100644 index 000000000..9adb267ad --- /dev/null +++ b/packages/ui/src/components/typewriter.tsx @@ -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> + ) +} diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index cea5a082d..146d957e2 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -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); |
