/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/app/src/components/dialog-release-notes.tsx
144 строки
5 KB
Adam
chore(app): i18n sync (#15362)
27 фев 2026, 18:45
Не верифицирован
27 фев 2026, 18:45
6ef3af7
Код
Авторство
О чём код?
import { createSignal } from "solid-js" import { Dialog } from "@opencode-ai/ui/dialog" import { Button } from "@opencode-ai/ui/button" import { useDialog } from "@opencode-ai/ui/context/dialog" import { useLanguage } from "@/context/language" import { useSettings } from "@/context/settings" export type Highlight = { title: string description: string media?: { type: "image" | "video" src: string alt?: string } } export function DialogReleaseNotes(props: { highlights: Highlight[] }) { const dialog = useDialog() const language = useLanguage() const settings = useSettings() const [index, setIndex] = createSignal(0) const total = () => props.highlights.length const last = () => Math.max(0, total() - 1) const feature = () => props.highlights[index()] ?? props.highlights[last()] const isFirst = () => index() === 0 const isLast = () => index() >= last() const paged = () => total() > 1 function handleNext() { if (isLast()) return setIndex(index() + 1) } function handleClose() { dialog.close() } function handleDisable() { settings.general.setReleaseNotes(false) handleClose() } function handleKeyDown(e: KeyboardEvent) { if (e.key === "Escape") { e.preventDefault() handleClose() return } if (!paged()) return if (e.key === "ArrowLeft" && !isFirst()) { e.preventDefault() setIndex(index() - 1) } if (e.key === "ArrowRight" && !isLast()) { e.preventDefault() setIndex(index() + 1) } } return ( <Dialog size="large" fit class="w-[min(calc(100vw-40px),720px)] h-[min(calc(100vh-40px),400px)] -mt-20 min-h-0 overflow-hidden" > <div class="flex flex-1 min-w-0 min-h-0" tabIndex={0} autofocus onKeyDown={handleKeyDown}> {/* Left side - Text content */} <div class="flex flex-col flex-1 min-w-0 p-8"> {/* Top section - feature content (fixed position from top) */} <div class="flex flex-col gap-2 pt-22"> <div class="flex items-center gap-2"> <h1 class="text-16-medium text-text-strong">{feature()?.title ?? ""}</h1> </div> <p class="text-14-regular text-text-base">{feature()?.description ?? ""}</p> </div> {/* Spacer to push buttons to bottom */} <div class="flex-1" /> {/* Bottom section - buttons and indicators (fixed position) */} <div class="flex flex-col gap-12"> <div class="flex flex-col items-start gap-3"> {isLast() ? ( <Button variant="primary" size="large" onClick={handleClose}> {language.t("dialog.releaseNotes.action.getStarted")} </Button> ) : ( <Button variant="secondary" size="large" onClick={handleNext}> {language.t("dialog.releaseNotes.action.next")} </Button> )} <Button variant="ghost" size="small" onClick={handleDisable}> {language.t("dialog.releaseNotes.action.hideFuture")} </Button> </div> {paged() && ( <div class="flex items-center gap-1.5 -my-2.5"> {props.highlights.map((_, i) => ( <button type="button" class="h-6 flex items-center cursor-pointer bg-transparent border-none p-0 transition-all duration-200" classList={{ "w-8": i === index(), "w-3": i !== index(), }} onClick={() => setIndex(i)} > <div class="w-full h-0.5 rounded-[1px] transition-colors duration-200" classList={{ "bg-icon-strong-base": i === index(), "bg-icon-weak-base": i !== index(), }} /> </button> ))} </div> )} </div> </div> {/* Right side - Media content (edge to edge) */} {feature()?.media && ( <div class="flex-1 min-w-0 bg-surface-base overflow-hidden rounded-r-xl"> {feature()!.media!.type === "image" ? ( <img src={feature()!.media!.src} alt={feature()!.media!.alt ?? feature()?.title ?? language.t("dialog.releaseNotes.media.alt")} class="w-full h-full object-cover" /> ) : ( <video src={feature()!.media!.src} autoplay loop muted playsinline class="w-full h-full object-cover" /> )} </div> )} </div> </Dialog> ) }