Compare commits

...
Author SHA1 Message Date
David Hill 8cac48ad1a feat(app): copy version from about settings 2026-09-30 12:47:00 -06:00
3 changed files with 99 additions and 6 deletions
+1
View File
@@ -1151,6 +1151,7 @@ export const dict = {
"settings.tab.about": "About",
"settings.about.version": "Version {{version}}",
"settings.about.devVersion": "development",
"settings.about.copyVersionFailed": "Failed to copy version",
"settings.about.license": "Released under the MIT License",
"settings.about.writtenBy": "Written by",
"settings.about.illustratedBy": "Illustrated by",
+50 -6
View File
@@ -1,7 +1,10 @@
import { createResource } from "solid-js"
import { Tooltip } from "@opencode/ui/tooltip"
import { createEffect, createResource, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
import { useLanguage } from "@/runtime/i18n/language"
import { usePlatform } from "@/runtime/platform/platform"
import { ExternalLink } from "@/runtime/platform/external-link"
import { showToast } from "@/shell/notifications/toast"
import legal from "./legal.svg"
import anomalyBrush from "./anomaly-brush.svg"
import { AnimatedWordmark } from "./animated-wordmark"
@@ -30,11 +33,36 @@ const illustrators = ["usrnk1", "ludvigrask_", "arvsrn", "iamdavidhill"] as cons
export function SettingsAbout(props: { active: boolean }) {
const language = useLanguage()
const platform = usePlatform()
const [copy, setCopy] = createStore({ pending: false, copied: false, forceCopied: false })
const version = () => platform.version ?? language.t("settings.about.devVersion")
const versionLabel = () => language.t("settings.about.version", { version: version() })
const [otherContributors] = createResource(
() => props.active || undefined,
() => loadOtherContributorCount(platform.fetch ?? fetch),
{ initialValue: FALLBACK_OTHER_CONTRIBUTORS },
)
createEffect(() => {
if (!copy.copied) return
const close = setTimeout(() => setCopy("forceCopied", false), 2000)
const reset = setTimeout(() => setCopy("copied", false), 2120)
onCleanup(() => {
clearTimeout(close)
clearTimeout(reset)
})
})
const copyVersion = async () => {
if (copy.pending) return
setCopy({ pending: true, copied: false, forceCopied: false })
await (platform.writeClipboardText?.(versionLabel()) ?? navigator.clipboard.writeText(versionLabel()))
.then(() => setCopy({ copied: true, forceCopied: true }))
.catch(() =>
showToast({
variant: "error",
title: language.t("settings.about.copyVersionFailed"),
}),
)
.finally(() => setCopy("pending", false))
}
const credit = (name: string) => (
<bdi dir="ltr">
<ExternalLink href={profile(name)}>{name}</ExternalLink>
@@ -52,11 +80,27 @@ export function SettingsAbout(props: { active: boolean }) {
return (
<div class="settings-about-content">
<div class="settings-about-intro">
<p>
{language.t("settings.about.version", {
version: platform.version ?? language.t("settings.about.devVersion"),
})}
</p>
<div class="settings-about-version-line">
<Tooltip
value={language.t(copy.copied ? "common.copied" : "ui.message.copy")}
placement="top"
forceOpen={copy.forceCopied ? true : undefined}
>
<button
type="button"
class="settings-about-version"
disabled={copy.pending}
aria-label={language.t(copy.copied ? "common.copied" : "ui.message.copy")}
onClick={() => void copyVersion()}
>
<span>
{language.rich("settings.about.version", {
version: <bdi dir="ltr">{version()}</bdi>,
})}
</span>
</button>
</Tooltip>
</div>
<p>{language.t("settings.about.license")}</p>
</div>
+48
View File
@@ -233,6 +233,54 @@
gap: 14px;
}
.settings-about-version-line {
display: flex;
align-items: center;
}
.settings-about-version {
position: relative;
isolation: isolate;
display: inline-flex;
min-height: 24px;
align-items: center;
margin-block: -2px;
padding: 0;
border: 0;
border-radius: 4px;
background: transparent;
color: inherit;
font: inherit;
cursor: pointer;
transition: color 120ms ease;
}
.settings-about-version::before {
position: absolute;
z-index: -1;
inset-block: 0;
inset-inline: -6px;
border-radius: 4px;
background: transparent;
content: "";
transition: background-color 120ms ease;
}
.settings-about-version:hover,
.settings-about-version:focus-visible {
color: var(--v2-text-text-base);
outline: none;
}
.settings-about-version:hover::before,
.settings-about-version:focus-visible::before {
background: var(--v2-background-bg-layer-02);
}
.settings-about-version:disabled {
cursor: default;
}
.settings-about-credits,
.settings-about-publication,
.settings-about-details {