From 93b9e47c05e846d45f172d4fb11c23636206eb5d Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 15:54:47 +0800 Subject: [PATCH 01/16] changelog v1 --- .../app/src/components/dialog-changelog.tsx | 137 ++++++++++++++++++ .../app/src/components/dialog-settings.tsx | 25 ++-- 2 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 packages/app/src/components/dialog-changelog.tsx diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx new file mode 100644 index 00000000000..af0e6cfaae4 --- /dev/null +++ b/packages/app/src/components/dialog-changelog.tsx @@ -0,0 +1,137 @@ +import { createSignal, onMount, For, Show } from "solid-js" +import { Dialog } from "@opencode-ai/ui/dialog" +import { Button } from "@opencode-ai/ui/button" +import { Markdown } from "@opencode-ai/ui/markdown" +import { useDialog } from "@opencode-ai/ui/context/dialog" +import { usePlatform } from "@/context/platform" + +const REPO = "anomalyco/opencode" +const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases` + +type Release = { + tag: string + body: string + date: string +} + +function parseReleases(json: unknown): Release[] { + const releases: Release[] = [] + + if (!Array.isArray(json)) { + return releases + } + + for (const release of json) { + if (!release || typeof release !== "object") continue + releases.push({ + tag: typeof release.tag_name === "string" ? release.tag_name : "Unknown", + body: typeof release.body === "string" ? release.body : "", + date: typeof release.published_at === "string" ? new Date(release.published_at).toLocaleDateString() : "", + }) + } + + return releases +} + +export function DialogChangelog() { + const dialog = useDialog() + const platform = usePlatform() + const [releases, setReleases] = createSignal([]) + const [loading, setLoading] = createSignal(true) + const [loadingMore, setLoadingMore] = createSignal(false) + const [error, setError] = createSignal(undefined) + const [page, setPage] = createSignal(1) + const [hasMore, setHasMore] = createSignal(true) + const PER_PAGE = 10 + + async function loadReleases(reset = false) { + const currentPage = reset ? 1 : page() + const isLoading = reset ? setLoading : setLoadingMore + + try { + isLoading(true) + const fetcher = platform.fetch ?? fetch + const response = await fetcher(`${GITHUB_API_URL}?per_page=${PER_PAGE}&page=${currentPage}`, { + headers: { Accept: "application/vnd.github.v3+json" }, + }) + if (!response.ok) throw new Error("Failed to load") + + const json = await response.json() + const parsed = parseReleases(json) + + if (parsed.length < PER_PAGE) { + setHasMore(false) + } + + if (reset) { + setReleases(parsed) + } else { + setReleases((prev) => [...prev, ...parsed]) + } + } catch { + setError("Failed to load changelog") + } finally { + setLoading(false) + setLoadingMore(false) + } + } + + onMount(() => loadReleases()) + + function handleClose() { + dialog.close() + } + + function handleLoadMore() { + setPage((p) => p + 1) + loadReleases() + } + + return ( + +
+
+

Changelog

+ +
+
+ +

Loading...

+
+ +

{error()}

+
+ +

No releases found.

+
+ 0}> +
+ + {(release) => ( +
+
+ {release.tag} + + {release.date} + +
+ +
+ )} +
+
+ +
+ +
+
+
+
+
+
+ ) +} diff --git a/packages/app/src/components/dialog-settings.tsx b/packages/app/src/components/dialog-settings.tsx index f8892ebbdc8..4500b442194 100644 --- a/packages/app/src/components/dialog-settings.tsx +++ b/packages/app/src/components/dialog-settings.tsx @@ -2,16 +2,24 @@ import { Component } from "solid-js" import { Dialog } from "@opencode-ai/ui/dialog" import { Tabs } from "@opencode-ai/ui/tabs" import { Icon } from "@opencode-ai/ui/icon" +import { Button } from "@opencode-ai/ui/button" import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" +import { useDialog } from "@opencode-ai/ui/context/dialog" import { SettingsGeneral } from "./settings-general" import { SettingsKeybinds } from "./settings-keybinds" import { SettingsProviders } from "./settings-providers" import { SettingsModels } from "./settings-models" +import { DialogChangelog } from "@/components/dialog-changelog" export const DialogSettings: Component = () => { const language = useLanguage() const platform = usePlatform() + const dialog = useDialog() + + function handleShowChangelog() { + dialog.show(() => ) + } return ( @@ -52,6 +60,14 @@ export const DialogSettings: Component = () => {
{language.t("app.name.desktop")} v{platform.version} +
@@ -67,15 +83,6 @@ export const DialogSettings: Component = () => { - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */}
) From 62fa5c13140688d50a8136a8bd30e26ea83e70ab Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 16:28:09 +0800 Subject: [PATCH 02/16] fix styles --- .../app/src/components/dialog-changelog.css | 38 +++++++++++++++++++ .../app/src/components/dialog-changelog.tsx | 14 ++++--- 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 packages/app/src/components/dialog-changelog.css diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css new file mode 100644 index 00000000000..fd484a0f248 --- /dev/null +++ b/packages/app/src/components/dialog-changelog.css @@ -0,0 +1,38 @@ +.dialog-changelog { + height: min(calc(100vh - 80px), 600px); + width: min(calc(100vw - 80px), 720px); +} + +.dialog-changelog-content { + min-height: 0; +} + +.dialog-changelog-version { + font-size: 20px; + font-weight: 600; +} + +.dialog-changelog-release { + border-top: 2px solid var(--border-base); + padding-top: 24px; + margin-top: 16px; +} + +.dialog-changelog-release:first-child { + border-top: none; + padding-top: 0; + margin-top: 0; +} + +.dialog-changelog-markdown h2 { + border-bottom: 1px solid var(--border-weak-base); + padding-bottom: 4px; + margin: 24px 0 8px 0; + font-size: 14px; + font-weight: 500; + text-transform: capitalize; +} + +.dialog-changelog-markdown h2:first-child { + margin-top: 0; +} diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index af0e6cfaae4..1d48fec07f9 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -4,6 +4,7 @@ import { Button } from "@opencode-ai/ui/button" import { Markdown } from "@opencode-ai/ui/markdown" import { useDialog } from "@opencode-ai/ui/context/dialog" import { usePlatform } from "@/context/platform" +import "./dialog-changelog.css" const REPO = "anomalyco/opencode" const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases` @@ -88,7 +89,7 @@ export function DialogChangelog() { } return ( - +

Changelog

@@ -96,7 +97,7 @@ export function DialogChangelog() { Close
-
+

Loading...

@@ -110,14 +111,17 @@ export function DialogChangelog() {
{(release) => ( -
+
- {release.tag} + {release.tag} {release.date}
- +
)} From 991e823039d91e03fd1d155a5c94a41862878e74 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 16:39:01 +0800 Subject: [PATCH 03/16] style fixes --- .../app/src/components/dialog-changelog.css | 19 +++++++++---------- .../app/src/components/dialog-changelog.tsx | 7 +------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index fd484a0f248..816b8f7e01d 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -1,8 +1,3 @@ -.dialog-changelog { - height: min(calc(100vh - 80px), 600px); - width: min(calc(100vw - 80px), 720px); -} - .dialog-changelog-content { min-height: 0; } @@ -13,9 +8,9 @@ } .dialog-changelog-release { - border-top: 2px solid var(--border-base); - padding-top: 24px; - margin-top: 16px; + border-bottom: 1px solid var(--border-weak-base); + margin-top: 64px; + padding-bottom: 24px; } .dialog-changelog-release:first-child { @@ -27,12 +22,16 @@ .dialog-changelog-markdown h2 { border-bottom: 1px solid var(--border-weak-base); padding-bottom: 4px; - margin: 24px 0 8px 0; + margin: 32px 0 12px 0; font-size: 14px; font-weight: 500; text-transform: capitalize; } .dialog-changelog-markdown h2:first-child { - margin-top: 0; + margin-top: 16px; +} + +.dialog-changelog-markdown { + margin-top: 8px; } diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index 1d48fec07f9..310486a9816 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -91,12 +91,7 @@ export function DialogChangelog() { return (
-
-

Changelog

- -
+

Loading...

From 1abc228e95771e93cb949b3c1793ba8304abce81 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 16:52:19 +0800 Subject: [PATCH 04/16] add timetsamps --- packages/app/src/components/dialog-changelog.tsx | 4 ++-- packages/app/src/utils/time.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 packages/app/src/utils/time.ts diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index 310486a9816..eec097761a6 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -4,6 +4,7 @@ import { Button } from "@opencode-ai/ui/button" import { Markdown } from "@opencode-ai/ui/markdown" import { useDialog } from "@opencode-ai/ui/context/dialog" import { usePlatform } from "@/context/platform" +import { getRelativeTime } from "@/utils/time" import "./dialog-changelog.css" const REPO = "anomalyco/opencode" @@ -27,7 +28,7 @@ function parseReleases(json: unknown): Release[] { releases.push({ tag: typeof release.tag_name === "string" ? release.tag_name : "Unknown", body: typeof release.body === "string" ? release.body : "", - date: typeof release.published_at === "string" ? new Date(release.published_at).toLocaleDateString() : "", + date: typeof release.published_at === "string" ? getRelativeTime(release.published_at) : "", }) } @@ -91,7 +92,6 @@ export function DialogChangelog() { return (
-

Loading...

diff --git a/packages/app/src/utils/time.ts b/packages/app/src/utils/time.ts new file mode 100644 index 00000000000..b798ff52904 --- /dev/null +++ b/packages/app/src/utils/time.ts @@ -0,0 +1,15 @@ +export function getRelativeTime(dateString: string): string { + const date = new Date(dateString) + const now = new Date() + const diffMs = now.getTime() - date.getTime() + const diffSeconds = Math.floor(diffMs / 1000) + const diffMinutes = Math.floor(diffSeconds / 60) + const diffHours = Math.floor(diffMinutes / 60) + const diffDays = Math.floor(diffHours / 24) + + if (diffSeconds < 60) return "just now" + if (diffMinutes < 60) return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago` + if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago` + if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? "" : "s"} ago` + return date.toLocaleDateString() +} From 4503bde1cced0c02094d3eeeb5b7c403a1ce6ec9 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 16:59:00 +0800 Subject: [PATCH 05/16] fix styling, add scrollbar --- .../app/src/components/dialog-changelog.css | 29 ++++++++++++++++++- .../app/src/components/dialog-changelog.tsx | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index 816b8f7e01d..ce6783919bb 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -1,7 +1,34 @@ -.dialog-changelog-content { +.dialog-changelog { min-height: 0; } +.dialog-changelog-content { + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: var(--border-weak-base) transparent; +} + +.dialog-changelog-content::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +.dialog-changelog-content::-webkit-scrollbar-track { + background: transparent; + border-radius: 5px; +} + +.dialog-changelog-content::-webkit-scrollbar-thumb { + background: var(--border-weak-base); + border-radius: 5px; + border: 3px solid transparent; + background-clip: padding-box; +} + +.dialog-changelog-content::-webkit-scrollbar-thumb:hover { + background: var(--border-weak-base); +} + .dialog-changelog-version { font-size: 20px; font-weight: 600; diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index eec097761a6..c99499ace76 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -92,7 +92,7 @@ export function DialogChangelog() { return (
-
+

Loading...

From 72eec2043780b6769370f8a069769108dcc4ec1f Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 17:12:41 +0800 Subject: [PATCH 06/16] add links --- .../app/src/components/dialog-changelog.css | 27 +++++++++++++++++++ .../app/src/components/dialog-changelog.tsx | 17 +++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index ce6783919bb..94718b942b1 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -62,3 +62,30 @@ .dialog-changelog-markdown { margin-top: 8px; } + +.dialog-changelog-markdown a[href^="https://github.com/anomalyco/opencode/issues/"] +{ + color: var(--text-interactive-base); + font-weight: 500; + text-decoration: none; + border-radius: 3px; + padding: 0 2px; +} + +.dialog-changelog-markdown a[href^="https://github.com/anomalyco/opencode/issues/"]:hover +{ + background: var(--surface-weak-base); + text-decoration: none; +} + +.dialog-changelog-markdown a[href^="https://github.com/"][href*="/"] +{ + color: var(--text-interactive-base); + font-weight: 500; + text-decoration: none; +} + +.dialog-changelog-markdown a[href^="https://github.com/"][href*="/"]:hover +{ + text-decoration: underline; +} diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index c99499ace76..be1c2ff2d89 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -16,6 +16,20 @@ type Release = { date: string } +function transformGitHubReferences(body: string): string { + let result = body + + result = result.replace(/#(\d+)/g, (_, id) => { + return `[#${id}](https://github.com/anomalyco/opencode/issues/${id})` + }) + + result = result.replace(/@([a-zA-Z0-9_-]+)/g, (_, username) => { + return `[@${username}](https://github.com/${username})` + }) + + return result +} + function parseReleases(json: unknown): Release[] { const releases: Release[] = [] @@ -25,9 +39,10 @@ function parseReleases(json: unknown): Release[] { for (const release of json) { if (!release || typeof release !== "object") continue + const body = typeof release.body === "string" ? transformGitHubReferences(release.body) : "" releases.push({ tag: typeof release.tag_name === "string" ? release.tag_name : "Unknown", - body: typeof release.body === "string" ? release.body : "", + body, date: typeof release.published_at === "string" ? getRelativeTime(release.published_at) : "", }) } From d364c439168de955053f14117d5b9a97e5a72783 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 20:15:02 +0800 Subject: [PATCH 07/16] style fixes --- .../app/src/components/dialog-changelog.css | 122 +++++++++++++----- .../app/src/components/dialog-changelog.tsx | 68 ++++------ packages/app/src/components/release-list.tsx | 66 ++++++++++ 3 files changed, 182 insertions(+), 74 deletions(-) create mode 100644 packages/app/src/components/release-list.tsx diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index 94718b942b1..f3f50bfda13 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -2,48 +2,114 @@ min-height: 0; } -.dialog-changelog-content { +.dialog-changelog [data-slot="dialog-body"] { + flex: 1; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.dialog-changelog-list { + flex: 1; + overflow: hidden; +} + +.dialog-changelog-list [data-slot="list-scroll"] { + height: 100%; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-weak-base) transparent; } -.dialog-changelog-content::-webkit-scrollbar { +.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar { width: 10px; height: 10px; } -.dialog-changelog-content::-webkit-scrollbar-track { +.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar-track { background: transparent; border-radius: 5px; } -.dialog-changelog-content::-webkit-scrollbar-thumb { +.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar-thumb { background: var(--border-weak-base); border-radius: 5px; border: 3px solid transparent; background-clip: padding-box; } -.dialog-changelog-content::-webkit-scrollbar-thumb:hover { +.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar-thumb:hover { background: var(--border-weak-base); } +.dialog-changelog-header { + padding: 8px 12px 8px 8px; + display: flex; + align-items: baseline; + gap: 8px; + position: sticky; + top: 0; + z-index: 10; + background: var(--surface-raised-stronger-non-alpha); +} + +.dialog-changelog-header::after { + content: ""; + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 16px; + background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent); + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; +} + +.dialog-changelog-header[data-stuck="true"]::after { + opacity: 1; +} + +.dialog-changelog-divider { + border-bottom: 1px solid var(--border-weak-base); +} + .dialog-changelog-version { font-size: 20px; font-weight: 600; } -.dialog-changelog-release { - border-bottom: 1px solid var(--border-weak-base); - margin-top: 64px; - padding-bottom: 24px; +.dialog-changelog-date { + font-size: 12px; + font-weight: 400; + color: var(--text-weak); +} + +.dialog-changelog-list [data-slot="list-item"] { + margin: 0; + padding: 0; + border: none; + background: transparent; + cursor: default; + display: block; + text-align: left; +} + +.dialog-changelog-list [data-slot="list-item"]:hover { + background: transparent; +} + +.dialog-changelog-list [data-slot="list-item"]:focus { + outline: none; +} + +.dialog-changelog-list [data-slot="list-item"]:focus-visible { + outline: 2px solid var(--focus-base); + outline-offset: 2px; } -.dialog-changelog-release:first-child { - border-top: none; - padding-top: 0; - margin-top: 0; +.dialog-changelog-content { + padding: 0 8px 24px; } .dialog-changelog-markdown h2 { @@ -59,33 +125,27 @@ margin-top: 16px; } -.dialog-changelog-markdown { - margin-top: 8px; -} - -.dialog-changelog-markdown a[href^="https://github.com/anomalyco/opencode/issues/"] -{ +.dialog-changelog-markdown a.external-link { color: var(--text-interactive-base); font-weight: 500; text-decoration: none; - border-radius: 3px; - padding: 0 2px; } -.dialog-changelog-markdown a[href^="https://github.com/anomalyco/opencode/issues/"]:hover -{ - background: var(--surface-weak-base); - text-decoration: none; +.dialog-changelog-markdown .external-link:hover { + text-decoration: none !important; } -.dialog-changelog-markdown a[href^="https://github.com/"][href*="/"] +.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/pull/"], +.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/issues/"], +.dialog-changelog-markdown a.external-link[href^="https://github.com/"] { - color: var(--text-interactive-base); - font-weight: 500; - text-decoration: none; + border-radius: 3px; + padding: 0 2px; } -.dialog-changelog-markdown a[href^="https://github.com/"][href*="/"]:hover +.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/pull/"]:hover, +.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/issues/"]:hover, +.dialog-changelog-markdown a.external-link[href^="https://github.com/"]:hover { - text-decoration: underline; + background: var(--surface-weak-base); } diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index be1c2ff2d89..29d97a613b0 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -1,10 +1,10 @@ -import { createSignal, onMount, For, Show } from "solid-js" +import { createSignal, onMount, Show } from "solid-js" import { Dialog } from "@opencode-ai/ui/dialog" import { Button } from "@opencode-ai/ui/button" -import { Markdown } from "@opencode-ai/ui/markdown" import { useDialog } from "@opencode-ai/ui/context/dialog" import { usePlatform } from "@/context/platform" import { getRelativeTime } from "@/utils/time" +import { ReleaseList } from "@/components/release-list" import "./dialog-changelog.css" const REPO = "anomalyco/opencode" @@ -105,47 +105,29 @@ export function DialogChangelog() { } return ( - -
-
- -

Loading...

-
- -

{error()}

-
- -

No releases found.

-
- 0}> -
- - {(release) => ( -
-
- {release.tag} - - {release.date} - -
- -
- )} -
-
- -
- -
-
-
-
-
+ + +

Loading...

+
+ +

{error()}

+
+ +

No releases found.

+
+ 0}> + + +
+ +
+
+
) } diff --git a/packages/app/src/components/release-list.tsx b/packages/app/src/components/release-list.tsx new file mode 100644 index 00000000000..46b7c32d58d --- /dev/null +++ b/packages/app/src/components/release-list.tsx @@ -0,0 +1,66 @@ +import { Component, createEffect, createSignal, onCleanup } from "solid-js" +import { List } from "@opencode-ai/ui/list" +import { Markdown } from "@opencode-ai/ui/markdown" +import "./dialog-changelog.css" + +type Release = { + tag: string + body: string + date: string +} + +interface ReleaseListProps { + releases: Release[] +} + +function StickyHeader(props: { tag: string; date: string }) { + const [stuck, setStuck] = createSignal(false) + const [header, setHeader] = createSignal(undefined) + + const scrollEl = document.querySelector('[data-slot="list-scroll"]') as HTMLElement | null + + createEffect(() => { + const node = header() + if (!scrollEl || !node) return + + const handler = () => { + const rect = node.getBoundingClientRect() + const scrollRect = scrollEl.getBoundingClientRect() + setStuck(rect.top <= scrollRect.top + 1 && scrollEl.scrollTop > 0) + } + + scrollEl.addEventListener("scroll", handler, { passive: true }) + handler() + onCleanup(() => scrollEl.removeEventListener("scroll", handler)) + }) + + return ( +
+ {props.tag} + {props.date} +
+ ) +} + +export const ReleaseList: Component = (props) => { + return ( + x.tag} + search={false} + emptyMessage="No releases found" + loadingMessage="Loading..." + class="dialog-changelog-list" + > + {(item) => ( + <> + +
+
+ +
+ + )} + + ) +} From 1587d93b299649ce2912b66e83a7e6c9375d9ced Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 20:35:06 +0800 Subject: [PATCH 08/16] fixes --- .../app/src/components/dialog-changelog.css | 5 ----- .../app/src/components/dialog-changelog.tsx | 19 +++++++------------ packages/app/src/components/release-list.tsx | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index f3f50bfda13..9b3c4df09e4 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -128,11 +128,6 @@ .dialog-changelog-markdown a.external-link { color: var(--text-interactive-base); font-weight: 500; - text-decoration: none; -} - -.dialog-changelog-markdown .external-link:hover { - text-decoration: none !important; } .dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/pull/"], diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index 29d97a613b0..b9e23adbdcf 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -105,10 +105,7 @@ export function DialogChangelog() { } return ( - +

Loading...

@@ -119,14 +116,12 @@ export function DialogChangelog() {

No releases found.

0}> - - -
- -
-
+
) diff --git a/packages/app/src/components/release-list.tsx b/packages/app/src/components/release-list.tsx index 46b7c32d58d..da3a5cec83a 100644 --- a/packages/app/src/components/release-list.tsx +++ b/packages/app/src/components/release-list.tsx @@ -1,6 +1,7 @@ import { Component, createEffect, createSignal, onCleanup } from "solid-js" import { List } from "@opencode-ai/ui/list" import { Markdown } from "@opencode-ai/ui/markdown" +import { Button } from "@opencode-ai/ui/button" import "./dialog-changelog.css" type Release = { @@ -11,6 +12,9 @@ type Release = { interface ReleaseListProps { releases: Release[] + hasMore: boolean + loadingMore: boolean + onLoadMore: () => void } function StickyHeader(props: { tag: string; date: string }) { @@ -51,6 +55,16 @@ export const ReleaseList: Component = (props) => { emptyMessage="No releases found" loadingMessage="Loading..." class="dialog-changelog-list" + add={{ + render: () => + props.hasMore ? ( +
+ +
+ ) : null, + }} > {(item) => ( <> From 954d31903f92652d8c5fba0d2f6e329b6f0bba4a Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 20:50:55 +0800 Subject: [PATCH 09/16] fix ui --- .../app/src/components/dialog-changelog.css | 15 +++++--- .../app/src/components/dialog-changelog.tsx | 38 ++++++++++--------- .../app/src/components/dialog-settings.tsx | 8 ++-- packages/app/src/components/release-list.tsx | 3 +- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index 9b3c4df09e4..e5d54c25541 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -1,5 +1,7 @@ .dialog-changelog { - min-height: 0; + min-height: 500px; + display: flex; + flex-direction: column; } .dialog-changelog [data-slot="dialog-body"] { @@ -7,15 +9,19 @@ overflow: hidden; display: flex; flex-direction: column; + min-height: 0; } .dialog-changelog-list { flex: 1; overflow: hidden; + display: flex; + flex-direction: column; + min-height: 0; } .dialog-changelog-list [data-slot="list-scroll"] { - height: 100%; + flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-weak-base) transparent; @@ -44,6 +50,7 @@ .dialog-changelog-header { padding: 8px 12px 8px 8px; + margin-top: 32px; display: flex; align-items: baseline; gap: 8px; @@ -70,9 +77,7 @@ opacity: 1; } -.dialog-changelog-divider { - border-bottom: 1px solid var(--border-weak-base); -} + .dialog-changelog-version { font-size: 20px; diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index b9e23adbdcf..5cd68a4b83c 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -105,24 +105,26 @@ export function DialogChangelog() { } return ( - - -

Loading...

-
- -

{error()}

-
- -

No releases found.

-
- 0}> - - + +
+ +

Loading...

+
+ +

{error()}

+
+ +

No releases found.

+
+ 0}> + + +
) } diff --git a/packages/app/src/components/dialog-settings.tsx b/packages/app/src/components/dialog-settings.tsx index 4500b442194..6fd02101a51 100644 --- a/packages/app/src/components/dialog-settings.tsx +++ b/packages/app/src/components/dialog-settings.tsx @@ -60,14 +60,12 @@ export const DialogSettings: Component = () => {
{language.t("app.name.desktop")} v{platform.version} - +
diff --git a/packages/app/src/components/release-list.tsx b/packages/app/src/components/release-list.tsx index da3a5cec83a..cbc9d1bfe1a 100644 --- a/packages/app/src/components/release-list.tsx +++ b/packages/app/src/components/release-list.tsx @@ -54,7 +54,7 @@ export const ReleaseList: Component = (props) => { search={false} emptyMessage="No releases found" loadingMessage="Loading..." - class="dialog-changelog-list" + class="dialog-changelog-list flex-1 min-h-0" add={{ render: () => props.hasMore ? ( @@ -69,7 +69,6 @@ export const ReleaseList: Component = (props) => { {(item) => ( <> -
From d8bcfd90d36e90c1a10e1a416e6373bc7e86afcd Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 4 Feb 2026 21:06:27 +0800 Subject: [PATCH 10/16] trnslations, couple more fixes --- packages/app/src/components/dialog-changelog.css | 3 +-- packages/app/src/components/dialog-changelog.tsx | 6 ++++-- packages/app/src/components/release-list.tsx | 7 +++++-- packages/app/src/i18n/ar.ts | 2 ++ packages/app/src/i18n/br.ts | 2 ++ packages/app/src/i18n/da.ts | 2 ++ packages/app/src/i18n/de.ts | 2 ++ packages/app/src/i18n/en.ts | 2 ++ packages/app/src/i18n/es.ts | 2 ++ packages/app/src/i18n/fr.ts | 2 ++ packages/app/src/i18n/ja.ts | 2 ++ packages/app/src/i18n/ko.ts | 2 ++ packages/app/src/i18n/no.ts | 2 ++ packages/app/src/i18n/pl.ts | 2 ++ packages/app/src/i18n/ru.ts | 2 ++ packages/app/src/i18n/th.ts | 2 ++ packages/app/src/i18n/zh.ts | 2 ++ packages/app/src/i18n/zht.ts | 2 ++ 18 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.css b/packages/app/src/components/dialog-changelog.css index e5d54c25541..68fd9281345 100644 --- a/packages/app/src/components/dialog-changelog.css +++ b/packages/app/src/components/dialog-changelog.css @@ -50,7 +50,6 @@ .dialog-changelog-header { padding: 8px 12px 8px 8px; - margin-top: 32px; display: flex; align-items: baseline; gap: 8px; @@ -91,7 +90,7 @@ } .dialog-changelog-list [data-slot="list-item"] { - margin: 0; + margin-bottom: 32px; padding: 0; border: none; background: transparent; diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index 5cd68a4b83c..ea8a62311eb 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -2,6 +2,7 @@ import { createSignal, onMount, Show } 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 { usePlatform } from "@/context/platform" import { getRelativeTime } from "@/utils/time" import { ReleaseList } from "@/components/release-list" @@ -52,6 +53,7 @@ function parseReleases(json: unknown): Release[] { export function DialogChangelog() { const dialog = useDialog() + const language = useLanguage() const platform = usePlatform() const [releases, setReleases] = createSignal([]) const [loading, setLoading] = createSignal(true) @@ -108,13 +110,13 @@ export function DialogChangelog() {
-

Loading...

+

{language.t("common.loading")}...

{error()}

-

No releases found.

+

{language.t("common.noReleasesFound")}

0}> = (props) => { + const language = useLanguage() + return ( x.tag} search={false} emptyMessage="No releases found" - loadingMessage="Loading..." + loadingMessage={language.t("common.loading")} class="dialog-changelog-list flex-1 min-h-0" add={{ render: () => props.hasMore ? (
) : null, diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 6af69c8dfdb..b327f4bd6b6 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -501,6 +501,8 @@ export const dict = { "common.close": "إغلاق", "common.edit": "تحرير", "common.loadMore": "تحميل المزيد", + "common.changelog": "التغييرات", + "common.noReleasesFound": "لم يتم العثور على إصدارات", "common.key.esc": "ESC", "sidebar.menu.toggle": "تبديل القائمة", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index fa879eef037..41aefd248c8 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -505,6 +505,8 @@ export const dict = { "common.close": "Fechar", "common.edit": "Editar", "common.loadMore": "Carregar mais", + "common.changelog": "Novidades", + "common.noReleasesFound": "Nenhuma release encontrada", "common.key.esc": "ESC", "sidebar.menu.toggle": "Alternar menu", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index 390ac4c4c4e..28e4440d104 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -505,6 +505,8 @@ export const dict = { "common.close": "Luk", "common.edit": "Rediger", "common.loadMore": "Indlæs flere", + "common.changelog": "Nyheder", + "common.noReleasesFound": "Ingen versioner fundet", "common.key.esc": "ESC", "sidebar.menu.toggle": "Skift menu", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index e1f87a5edca..bdcb83bafa4 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -509,6 +509,8 @@ export const dict = { "common.close": "Schließen", "common.edit": "Bearbeiten", "common.loadMore": "Mehr laden", + "common.changelog": "Neuerungen", + "common.noReleasesFound": "Keine Versionen gefunden", "common.key.esc": "ESC", "sidebar.menu.toggle": "Menü umschalten", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index fb71a578af6..64d020f722c 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -516,6 +516,8 @@ export const dict = { "common.close": "Close", "common.edit": "Edit", "common.loadMore": "Load more", + "common.changelog": "Changelog", + "common.noReleasesFound": "No releases found", "common.key.esc": "ESC", "sidebar.menu.toggle": "Toggle menu", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 07b9af7e755..80d0e9f8f98 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -508,6 +508,8 @@ export const dict = { "common.close": "Cerrar", "common.edit": "Editar", "common.loadMore": "Cargar más", + "common.changelog": "Novedades", + "common.noReleasesFound": "No se encontraron versiones", "common.key.esc": "ESC", "sidebar.menu.toggle": "Alternar menú", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index c0411317981..4ba2246ddf6 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -513,6 +513,8 @@ export const dict = { "common.close": "Fermer", "common.edit": "Modifier", "common.loadMore": "Charger plus", + "common.changelog": "Nouveautés", + "common.noReleasesFound": "Aucune version trouvée", "common.key.esc": "ESC", "sidebar.menu.toggle": "Basculer le menu", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 5cb9a1502d2..0f365a988d4 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -500,6 +500,8 @@ export const dict = { "common.close": "閉じる", "common.edit": "編集", "common.loadMore": "さらに読み込む", + "common.changelog": "更新履歴", + "common.noReleasesFound": "バージョンが見つかりません", "common.key.esc": "ESC", "sidebar.menu.toggle": "メニューを切り替え", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 05918acba84..21c9b1052e0 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -506,6 +506,8 @@ export const dict = { "common.close": "닫기", "common.edit": "편집", "common.loadMore": "더 불러오기", + "common.changelog": "새로운 기능", + "common.noReleasesFound": "버전을 찾을 수 없음", "common.key.esc": "ESC", "sidebar.menu.toggle": "메뉴 토글", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 4e0fbf64d38..219f120c98b 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -508,6 +508,8 @@ export const dict = { "common.close": "Lukk", "common.edit": "Rediger", "common.loadMore": "Last flere", + "common.changelog": "Nyheter", + "common.noReleasesFound": "Ingen versjoner funnet", "common.key.esc": "ESC", "sidebar.menu.toggle": "Veksle meny", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index e395863472c..697319e5a24 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -507,6 +507,8 @@ export const dict = { "common.close": "Zamknij", "common.edit": "Edytuj", "common.loadMore": "Załaduj więcej", + "common.changelog": "Nowości", + "common.noReleasesFound": "Nie znaleziono wersji", "common.key.esc": "ESC", "sidebar.menu.toggle": "Przełącz menu", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 301043f3a57..527d323b5e6 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -509,6 +509,8 @@ export const dict = { "common.close": "Закрыть", "common.edit": "Редактировать", "common.loadMore": "Загрузить ещё", + "common.changelog": "Что нового", + "common.noReleasesFound": "Версии не найдены", "common.key.esc": "ESC", "sidebar.menu.toggle": "Переключить меню", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 7947a9d491a..801d2b9d7a9 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -508,6 +508,8 @@ export const dict = { "common.close": "ปิด", "common.edit": "แก้ไข", "common.loadMore": "โหลดเพิ่มเติม", + "common.changelog": "อัปเดต", + "common.noReleasesFound": "ไม่พบเวอร์ชัน", "common.key.esc": "ESC", "sidebar.menu.toggle": "สลับเมนู", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index a9a94a1ad4e..65ce4290936 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -503,6 +503,8 @@ export const dict = { "common.close": "关闭", "common.edit": "编辑", "common.loadMore": "加载更多", + "common.changelog": "更新日志", + "common.noReleasesFound": "未找到版本", "common.key.esc": "ESC", "sidebar.menu.toggle": "切换菜单", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 5407cf477e0..e28a235a8ff 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -500,6 +500,8 @@ export const dict = { "common.close": "關閉", "common.edit": "編輯", "common.loadMore": "載入更多", + "common.changelog": "更新日誌", + "common.noReleasesFound": "未找到版本", "common.key.esc": "ESC", "sidebar.menu.toggle": "切換選單", From e514919cc420fb29c4da312a12e994a7d372604b Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Wed, 11 Feb 2026 22:23:39 +0800 Subject: [PATCH 11/16] changelog refactor --- .../app/src/components/dialog-changelog.tsx | 1 - packages/app/src/components/release-list.tsx | 52 ++++++------------- packages/app/src/i18n/ar.ts | 1 + packages/app/src/i18n/br.ts | 1 + packages/app/src/i18n/bs.ts | 3 ++ packages/app/src/i18n/da.ts | 1 + packages/app/src/i18n/de.ts | 1 + packages/app/src/i18n/en.ts | 1 + packages/app/src/i18n/es.ts | 1 + packages/app/src/i18n/fr.ts | 1 + packages/app/src/i18n/ja.ts | 1 + packages/app/src/i18n/ko.ts | 1 + packages/app/src/i18n/no.ts | 1 + packages/app/src/i18n/pl.ts | 1 + packages/app/src/i18n/ru.ts | 1 + packages/app/src/i18n/th.ts | 1 + packages/app/src/i18n/zh.ts | 1 + packages/app/src/i18n/zht.ts | 1 + 18 files changed, 33 insertions(+), 38 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index ea8a62311eb..68ce57f740b 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -6,7 +6,6 @@ import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" import { getRelativeTime } from "@/utils/time" import { ReleaseList } from "@/components/release-list" -import "./dialog-changelog.css" const REPO = "anomalyco/opencode" const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases` diff --git a/packages/app/src/components/release-list.tsx b/packages/app/src/components/release-list.tsx index c70328ee80b..a3e7b3e31df 100644 --- a/packages/app/src/components/release-list.tsx +++ b/packages/app/src/components/release-list.tsx @@ -1,9 +1,9 @@ -import { Component, createEffect, createSignal, onCleanup } from "solid-js" +import { Component } from "solid-js" import { List } from "@opencode-ai/ui/list" import { Markdown } from "@opencode-ai/ui/markdown" import { Button } from "@opencode-ai/ui/button" +import { Tag } from "@opencode-ai/ui/tag" import { useLanguage } from "@/context/language" -import "./dialog-changelog.css" type Release = { tag: string @@ -18,35 +18,6 @@ interface ReleaseListProps { onLoadMore: () => void } -function StickyHeader(props: { tag: string; date: string }) { - const [stuck, setStuck] = createSignal(false) - const [header, setHeader] = createSignal(undefined) - - const scrollEl = document.querySelector('[data-slot="list-scroll"]') as HTMLElement | null - - createEffect(() => { - const node = header() - if (!scrollEl || !node) return - - const handler = () => { - const rect = node.getBoundingClientRect() - const scrollRect = scrollEl.getBoundingClientRect() - setStuck(rect.top <= scrollRect.top + 1 && scrollEl.scrollTop > 0) - } - - scrollEl.addEventListener("scroll", handler, { passive: true }) - handler() - onCleanup(() => scrollEl.removeEventListener("scroll", handler)) - }) - - return ( -
- {props.tag} - {props.date} -
- ) -} - export const ReleaseList: Component = (props) => { const language = useLanguage() @@ -57,7 +28,7 @@ export const ReleaseList: Component = (props) => { search={false} emptyMessage="No releases found" loadingMessage={language.t("common.loading")} - class="dialog-changelog-list flex-1 min-h-0" + class="flex-1 min-h-0 overflow-hidden flex flex-col [&_[data-slot=list-scroll]]:session-scroller [&_[data-slot=list-item]]:block [&_[data-slot=list-item]]:p-0 [&_[data-slot=list-item]]:border-0 [&_[data-slot=list-item]]:bg-transparent [&_[data-slot=list-item]]:text-left [&_[data-slot=list-item]]:cursor-default [&_[data-slot=list-item]]:hover:bg-transparent [&_[data-slot=list-item]]:focus:outline-none" add={{ render: () => props.hasMore ? ( @@ -70,12 +41,19 @@ export const ReleaseList: Component = (props) => { }} > {(item) => ( - <> - -
- +
+
+ {item.tag} + {item.date} + {item.tag === props.releases[0]?.tag && {language.t("changelog.tag.latest")}} +
+
+
- +
)} ) diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 528ca11f33d..fd87c123f9f 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -491,6 +491,7 @@ export const dict = { "common.loadMore": "تحميل المزيد", "common.changelog": "التغييرات", "common.noReleasesFound": "لم يتم العثور على إصدارات", + "changelog.tag.latest": "الأحدث", "common.key.esc": "ESC", "sidebar.menu.toggle": "تبديل القائمة", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 776cf9ed03e..c7894aabdb5 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -495,6 +495,7 @@ export const dict = { "common.loadMore": "Carregar mais", "common.changelog": "Novidades", "common.noReleasesFound": "Nenhuma release encontrada", + "changelog.tag.latest": "Mais recente", "common.key.esc": "ESC", "sidebar.menu.toggle": "Alternar menu", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 05eca1628e5..595c2c02950 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -519,6 +519,9 @@ export const dict = { "common.close": "Zatvori", "common.edit": "Uredi", "common.loadMore": "Učitaj još", + "common.changelog": "Novosti", + "common.noReleasesFound": "Nema pronađenih verzija", + "changelog.tag.latest": "Najnovije", "common.key.esc": "ESC", "sidebar.menu.toggle": "Prikaži/sakrij meni", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index 05df745cccb..de53c88884b 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -495,6 +495,7 @@ export const dict = { "common.loadMore": "Indlæs flere", "common.changelog": "Nyheder", "common.noReleasesFound": "Ingen versioner fundet", + "changelog.tag.latest": "Seneste", "common.key.esc": "ESC", "sidebar.menu.toggle": "Skift menu", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 2a8e714a1b6..ad6f960aa4a 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -538,6 +538,7 @@ export const dict = { "common.loadMore": "Mehr laden", "common.changelog": "Neuerungen", "common.noReleasesFound": "Keine Versionen gefunden", + "changelog.tag.latest": "Neueste", "common.key.esc": "ESC", "sidebar.menu.toggle": "Menü umschalten", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 39e6176299c..2f93345d79f 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -565,6 +565,7 @@ export const dict = { "common.loadMore": "Load more", "common.changelog": "Changelog", "common.noReleasesFound": "No releases found", + "changelog.tag.latest": "Latest", "common.key.esc": "ESC", "sidebar.menu.toggle": "Toggle menu", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 9ad39944b85..19dd1ab62d4 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -498,6 +498,7 @@ export const dict = { "common.loadMore": "Cargar más", "common.changelog": "Novedades", "common.noReleasesFound": "No se encontraron versiones", + "changelog.tag.latest": "Último", "common.key.esc": "ESC", "sidebar.menu.toggle": "Alternar menú", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 9a85023a56e..29d4d5ff571 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -503,6 +503,7 @@ export const dict = { "common.loadMore": "Charger plus", "common.changelog": "Nouveautés", "common.noReleasesFound": "Aucune version trouvée", + "changelog.tag.latest": "Dernier", "common.key.esc": "ESC", "sidebar.menu.toggle": "Basculer le menu", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index e9d4115f32e..7879c0b8509 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -490,6 +490,7 @@ export const dict = { "common.loadMore": "さらに読み込む", "common.changelog": "更新履歴", "common.noReleasesFound": "バージョンが見つかりません", + "changelog.tag.latest": "最新", "common.key.esc": "ESC", "sidebar.menu.toggle": "メニューを切り替え", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 630b8915b2a..46e7bf1d5a2 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -496,6 +496,7 @@ export const dict = { "common.loadMore": "더 불러오기", "common.changelog": "새로운 기능", "common.noReleasesFound": "버전을 찾을 수 없음", + "changelog.tag.latest": "최신", "common.key.esc": "ESC", "sidebar.menu.toggle": "메뉴 토글", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index 8400d079d91..5843afb72e8 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -498,6 +498,7 @@ export const dict = { "common.loadMore": "Last flere", "common.changelog": "Nyheter", "common.noReleasesFound": "Ingen versjoner funnet", + "changelog.tag.latest": "Siste", "common.key.esc": "ESC", "sidebar.menu.toggle": "Veksle meny", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index a8c55db2d9b..8a4a792875e 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -497,6 +497,7 @@ export const dict = { "common.loadMore": "Załaduj więcej", "common.changelog": "Nowości", "common.noReleasesFound": "Nie znaleziono wersji", + "changelog.tag.latest": "Najnowszy", "common.key.esc": "ESC", "sidebar.menu.toggle": "Przełącz menu", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 82fb5378ddb..e9e21e42c38 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -499,6 +499,7 @@ export const dict = { "common.loadMore": "Загрузить ещё", "common.changelog": "Что нового", "common.noReleasesFound": "Версии не найдены", + "changelog.tag.latest": "Последний", "common.key.esc": "ESC", "sidebar.menu.toggle": "Переключить меню", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index f931153ea9e..d470fb282c0 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -498,6 +498,7 @@ export const dict = { "common.loadMore": "โหลดเพิ่มเติม", "common.changelog": "อัปเดต", "common.noReleasesFound": "ไม่พบเวอร์ชัน", + "changelog.tag.latest": "ล่าสุด", "common.key.esc": "ESC", "sidebar.menu.toggle": "สลับเมนู", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 947cd1a9f4d..c78ecd4bdff 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -531,6 +531,7 @@ export const dict = { "common.loadMore": "加载更多", "common.changelog": "更新日志", "common.noReleasesFound": "未找到版本", + "changelog.tag.latest": "最新", "common.key.esc": "ESC", "sidebar.menu.toggle": "切换菜单", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index f28055ecc4e..c309871b942 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -528,6 +528,7 @@ export const dict = { "common.loadMore": "載入更多", "common.changelog": "更新日誌", "common.noReleasesFound": "未找到版本", + "changelog.tag.latest": "最新", "common.key.esc": "ESC", "sidebar.menu.toggle": "切換選單", From 70b555472ebb77ea392e34149c9f80dd35979823 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Sat, 14 Feb 2026 01:13:28 +0800 Subject: [PATCH 12/16] refactor, add caching --- packages/app/src/api/releases.ts | 51 +++++++++ .../app/src/components/dialog-changelog.tsx | 102 ++---------------- 2 files changed, 60 insertions(+), 93 deletions(-) create mode 100644 packages/app/src/api/releases.ts diff --git a/packages/app/src/api/releases.ts b/packages/app/src/api/releases.ts new file mode 100644 index 00000000000..46ececaa395 --- /dev/null +++ b/packages/app/src/api/releases.ts @@ -0,0 +1,51 @@ +import type { Platform } from "@/context/platform" +import { getRelativeTime } from "@/utils/time" + +const REPO = "anomalyco/opencode" +const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases` +const PER_PAGE = 30 +const CACHE_TTL = 1000 * 60 * 30 +const CACHE_KEY = "opencode.releases" + +type Release = { + tag: string + body: string + date: string +} + +function loadCache() { + const raw = localStorage.getItem(CACHE_KEY) + return raw ? JSON.parse(raw) : null +} + +function saveCache(data: { releases: Release[]; timestamp: number }) { + localStorage.setItem(CACHE_KEY, JSON.stringify(data)) +} + +export async function fetchReleases(platform: Platform): Promise<{ releases: Release[] }> { + const now = Date.now() + const cached = loadCache() + + if (cached && now - cached.timestamp < CACHE_TTL) { + return { releases: cached.releases } + } + + const fetcher = platform.fetch ?? fetch + const res = await fetcher(`${GITHUB_API_URL}?per_page=${PER_PAGE}`, { + headers: { Accept: "application/vnd.github.v3+json" }, + }).then((r) => (r.ok ? r.json() : Promise.reject(new Error("Failed to load")))) + + const releases = (Array.isArray(res) ? res : []).map((r) => ({ + tag: r.tag_name ?? "Unknown", + body: (r.body ?? "") + .replace(/#(\d+)/g, (_: string, id: string) => `[#${id}](https://github.com/anomalyco/opencode/pull/${id})`) + .replace(/@([a-zA-Z0-9_-]+)/g, (_: string, u: string) => `[@${u}](https://github.com/${u})`), + date: r.published_at ? getRelativeTime(r.published_at) : "", + })) + + saveCache({ releases, timestamp: now }) + + return { releases } +} + +export type { Release } diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index 68ce57f740b..7d3bdd0a2ea 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -1,110 +1,31 @@ import { createSignal, onMount, Show } 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 { usePlatform } from "@/context/platform" -import { getRelativeTime } from "@/utils/time" +import { fetchReleases, type Release } from "@/api/releases" import { ReleaseList } from "@/components/release-list" -const REPO = "anomalyco/opencode" -const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases` - -type Release = { - tag: string - body: string - date: string -} - -function transformGitHubReferences(body: string): string { - let result = body - - result = result.replace(/#(\d+)/g, (_, id) => { - return `[#${id}](https://github.com/anomalyco/opencode/issues/${id})` - }) - - result = result.replace(/@([a-zA-Z0-9_-]+)/g, (_, username) => { - return `[@${username}](https://github.com/${username})` - }) - - return result -} - -function parseReleases(json: unknown): Release[] { - const releases: Release[] = [] - - if (!Array.isArray(json)) { - return releases - } - - for (const release of json) { - if (!release || typeof release !== "object") continue - const body = typeof release.body === "string" ? transformGitHubReferences(release.body) : "" - releases.push({ - tag: typeof release.tag_name === "string" ? release.tag_name : "Unknown", - body, - date: typeof release.published_at === "string" ? getRelativeTime(release.published_at) : "", - }) - } - - return releases -} - export function DialogChangelog() { const dialog = useDialog() const language = useLanguage() const platform = usePlatform() const [releases, setReleases] = createSignal([]) const [loading, setLoading] = createSignal(true) - const [loadingMore, setLoadingMore] = createSignal(false) const [error, setError] = createSignal(undefined) - const [page, setPage] = createSignal(1) - const [hasMore, setHasMore] = createSignal(true) - const PER_PAGE = 10 - - async function loadReleases(reset = false) { - const currentPage = reset ? 1 : page() - const isLoading = reset ? setLoading : setLoadingMore - - try { - isLoading(true) - const fetcher = platform.fetch ?? fetch - const response = await fetcher(`${GITHUB_API_URL}?per_page=${PER_PAGE}&page=${currentPage}`, { - headers: { Accept: "application/vnd.github.v3+json" }, - }) - if (!response.ok) throw new Error("Failed to load") - const json = await response.json() - const parsed = parseReleases(json) + async function loadReleases() { + const result = await fetchReleases(platform) + .then((r) => ({ releases: r.releases, error: undefined })) + .catch((e) => ({ releases: [], error: e instanceof Error ? e.message : "Failed to load changelog" })) - if (parsed.length < PER_PAGE) { - setHasMore(false) - } - - if (reset) { - setReleases(parsed) - } else { - setReleases((prev) => [...prev, ...parsed]) - } - } catch { - setError("Failed to load changelog") - } finally { - setLoading(false) - setLoadingMore(false) - } + setReleases(result.releases) + setError(result.error) + setLoading(false) } onMount(() => loadReleases()) - function handleClose() { - dialog.close() - } - - function handleLoadMore() { - setPage((p) => p + 1) - loadReleases() - } - return (
@@ -118,12 +39,7 @@ export function DialogChangelog() {

{language.t("common.noReleasesFound")}

0}> - + {}} />
From b9ca79f3b680eba230d0b38eb736987f263d1fe0 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Sun, 1 Mar 2026 03:11:15 +0800 Subject: [PATCH 13/16] refactor: use createResource + Suspense instead of manual signals, remove unused imports --- .../app/src/components/dialog-changelog.tsx | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/packages/app/src/components/dialog-changelog.tsx b/packages/app/src/components/dialog-changelog.tsx index 7d3bdd0a2ea..321a667bcca 100644 --- a/packages/app/src/components/dialog-changelog.tsx +++ b/packages/app/src/components/dialog-changelog.tsx @@ -1,46 +1,39 @@ -import { createSignal, onMount, Show } from "solid-js" +import { createResource, Suspense, ErrorBoundary, Show } from "solid-js" import { Dialog } from "@opencode-ai/ui/dialog" -import { useDialog } from "@opencode-ai/ui/context/dialog" import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" -import { fetchReleases, type Release } from "@/api/releases" +import { fetchReleases } from "@/api/releases" import { ReleaseList } from "@/components/release-list" export function DialogChangelog() { - const dialog = useDialog() const language = useLanguage() const platform = usePlatform() - const [releases, setReleases] = createSignal([]) - const [loading, setLoading] = createSignal(true) - const [error, setError] = createSignal(undefined) - - async function loadReleases() { - const result = await fetchReleases(platform) - .then((r) => ({ releases: r.releases, error: undefined })) - .catch((e) => ({ releases: [], error: e instanceof Error ? e.message : "Failed to load changelog" })) - - setReleases(result.releases) - setError(result.error) - setLoading(false) - } - - onMount(() => loadReleases()) + const [data] = createResource(() => fetchReleases(platform)) return (
- -

{language.t("common.loading")}...

-
- -

{error()}

-
- -

{language.t("common.noReleasesFound")}

-
- 0}> - {}} /> - + ( +

+ {e instanceof Error ? e.message : "Failed to load changelog"} +

+ )} + > + {language.t("common.loading")}...

}> + 0} + fallback={

{language.t("common.noReleasesFound")}

} + > + {}} + /> +
+
+
) From 9ea36ccd9d69ff56a7dd4402c68792050bd95d6a Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Sun, 1 Mar 2026 05:29:04 +0800 Subject: [PATCH 14/16] sync time.ts specific code to fix typecheck --- packages/app/src/api/releases.ts | 3 +-- packages/app/src/components/release-list.tsx | 3 ++- packages/app/src/i18n/ar.ts | 6 ++++++ packages/app/src/i18n/br.ts | 4 ++++ packages/app/src/i18n/bs.ts | 4 ++++ packages/app/src/i18n/da.ts | 4 ++++ packages/app/src/i18n/de.ts | 4 ++++ packages/app/src/i18n/en.ts | 4 ++++ packages/app/src/i18n/es.ts | 4 ++++ packages/app/src/i18n/fr.ts | 4 ++++ packages/app/src/i18n/ja.ts | 4 ++++ packages/app/src/i18n/ko.ts | 4 ++++ packages/app/src/i18n/no.ts | 4 ++++ packages/app/src/i18n/pl.ts | 4 ++++ packages/app/src/i18n/ru.ts | 4 ++++ packages/app/src/i18n/th.ts | 4 ++++ packages/app/src/i18n/zh.ts | 4 ++++ packages/app/src/i18n/zht.ts | 4 ++++ packages/app/src/utils/time.ts | 18 +++++++++++++----- 19 files changed, 82 insertions(+), 8 deletions(-) diff --git a/packages/app/src/api/releases.ts b/packages/app/src/api/releases.ts index 46ececaa395..45de7cb12dc 100644 --- a/packages/app/src/api/releases.ts +++ b/packages/app/src/api/releases.ts @@ -1,5 +1,4 @@ import type { Platform } from "@/context/platform" -import { getRelativeTime } from "@/utils/time" const REPO = "anomalyco/opencode" const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases` @@ -40,7 +39,7 @@ export async function fetchReleases(platform: Platform): Promise<{ releases: Rel body: (r.body ?? "") .replace(/#(\d+)/g, (_: string, id: string) => `[#${id}](https://github.com/anomalyco/opencode/pull/${id})`) .replace(/@([a-zA-Z0-9_-]+)/g, (_: string, u: string) => `[@${u}](https://github.com/${u})`), - date: r.published_at ? getRelativeTime(r.published_at) : "", + date: r.published_at ?? "", })) saveCache({ releases, timestamp: now }) diff --git a/packages/app/src/components/release-list.tsx b/packages/app/src/components/release-list.tsx index a3e7b3e31df..7de9314dda9 100644 --- a/packages/app/src/components/release-list.tsx +++ b/packages/app/src/components/release-list.tsx @@ -4,6 +4,7 @@ import { Markdown } from "@opencode-ai/ui/markdown" import { Button } from "@opencode-ai/ui/button" import { Tag } from "@opencode-ai/ui/tag" import { useLanguage } from "@/context/language" +import { getRelativeTime } from "@/utils/time" type Release = { tag: string @@ -44,7 +45,7 @@ export const ReleaseList: Component = (props) => {
{item.tag} - {item.date} + {item.date ? getRelativeTime(item.date, language.t) : ""} {item.tag === props.releases[0]?.tag && {language.t("changelog.tag.latest")}}
diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 8eddd263469..d92e3fecda9 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -509,6 +509,12 @@ export const dict = { "common.changelog": "التغييرات", "common.noReleasesFound": "لم يتم العثور على إصدارات", "changelog.tag.latest": "الأحدث", + + "common.time.justNow": "الآن", + "common.time.minutesAgo.short": "قبل {{count}} د", + "common.time.hoursAgo.short": "قبل {{count}} س", + "common.time.daysAgo.short": "قبل {{count}} ي", + "common.key.esc": "ESC", "sidebar.menu.toggle": "تبديل القائمة", "sidebar.nav.projectsAndSessions": "المشاريع والجلسات", diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 28cc6a6d4b4..6d48fa714e2 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -514,6 +514,10 @@ export const dict = { "common.loadMore": "Carregar mais", "common.changelog": "Novidades", "common.noReleasesFound": "Nenhuma release encontrada", + "common.time.justNow": "Agora mesmo", + "common.time.minutesAgo.short": "{{count}}m atrás", + "common.time.hoursAgo.short": "{{count}}h atrás", + "common.time.daysAgo.short": "{{count}}d atrás", "changelog.tag.latest": "Mais recente", "common.key.esc": "ESC", "sidebar.menu.toggle": "Alternar menu", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 68a56a34a1a..b336fdf2895 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -574,6 +574,10 @@ export const dict = { "common.loadMore": "Učitaj još", "common.changelog": "Novosti", "common.noReleasesFound": "Nema pronađenih verzija", + "common.time.justNow": "Upravo sada", + "common.time.minutesAgo.short": "prije {{count}} min", + "common.time.hoursAgo.short": "prije {{count}} h", + "common.time.daysAgo.short": "prije {{count}} d", "changelog.tag.latest": "Najnovije", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index 39b2ce87a98..1698deb277c 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -570,6 +570,10 @@ export const dict = { "common.loadMore": "Indlæs flere", "common.changelog": "Nyheder", "common.noReleasesFound": "Ingen versioner fundet", + "common.time.justNow": "Lige nu", + "common.time.minutesAgo.short": "{{count}}m siden", + "common.time.hoursAgo.short": "{{count}}t siden", + "common.time.daysAgo.short": "{{count}}d siden", "changelog.tag.latest": "Seneste", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 922a8d1decb..81965d3b0dd 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -522,6 +522,10 @@ export const dict = { "common.loadMore": "Mehr laden", "common.changelog": "Neuerungen", "common.noReleasesFound": "Keine Versionen gefunden", + "common.time.justNow": "Gerade eben", + "common.time.minutesAgo.short": "vor {{count}} Min", + "common.time.hoursAgo.short": "vor {{count}} Std", + "common.time.daysAgo.short": "vor {{count}} Tg", "changelog.tag.latest": "Neueste", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index bafcc2912b4..552b33640eb 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -576,6 +576,10 @@ export const dict = { "common.loadMore": "Load more", "common.changelog": "Changelog", "common.noReleasesFound": "No releases found", + "common.time.justNow": "Just now", + "common.time.minutesAgo.short": "{{count}}m ago", + "common.time.hoursAgo.short": "{{count}}h ago", + "common.time.daysAgo.short": "{{count}}d ago", "changelog.tag.latest": "Latest", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index fc5ec1a74c9..a92e61e7a11 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -577,6 +577,10 @@ export const dict = { "common.loadMore": "Cargar más", "common.changelog": "Novedades", "common.noReleasesFound": "No se encontraron versiones", + "common.time.justNow": "Justo ahora", + "common.time.minutesAgo.short": "hace {{count}} min", + "common.time.hoursAgo.short": "hace {{count}} h", + "common.time.daysAgo.short": "hace {{count}} d", "changelog.tag.latest": "Último", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index bc857568796..d2c71262b17 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -520,6 +520,10 @@ export const dict = { "common.loadMore": "Charger plus", "common.changelog": "Nouveautés", "common.noReleasesFound": "Aucune version trouvée", + "common.time.justNow": "À l'instant", + "common.time.minutesAgo.short": "il y a {{count}}m", + "common.time.hoursAgo.short": "il y a {{count}}h", + "common.time.daysAgo.short": "il y a {{count}}j", "changelog.tag.latest": "Dernier", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 22563d08e70..b2e450821d8 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -512,6 +512,10 @@ export const dict = { "common.loadMore": "さらに読み込む", "common.changelog": "更新履歴", "common.noReleasesFound": "バージョンが見つかりません", + "common.time.justNow": "たった今", + "common.time.minutesAgo.short": "{{count}} 分前", + "common.time.hoursAgo.short": "{{count}} 時間前", + "common.time.daysAgo.short": "{{count}} 日前", "changelog.tag.latest": "最新", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 178dcc9177f..fb531797b4d 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -513,6 +513,10 @@ export const dict = { "common.loadMore": "더 불러오기", "common.changelog": "새로운 기능", "common.noReleasesFound": "버전을 찾을 수 없음", + "common.time.justNow": "방금 전", + "common.time.minutesAgo.short": "{{count}}분 전", + "common.time.hoursAgo.short": "{{count}}시간 전", + "common.time.daysAgo.short": "{{count}}일 전", "changelog.tag.latest": "최신", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index cbd9ebf9e71..a0b2c3452bc 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -577,6 +577,10 @@ export const dict = { "common.loadMore": "Last flere", "common.changelog": "Nyheter", "common.noReleasesFound": "Ingen versjoner funnet", + "common.time.justNow": "Akkurat nå", + "common.time.minutesAgo.short": "{{count}} m siden", + "common.time.hoursAgo.short": "{{count}} t siden", + "common.time.daysAgo.short": "{{count}} d siden", "changelog.tag.latest": "Siste", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index b9c61c876ba..5921dd11824 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -513,6 +513,10 @@ export const dict = { "common.loadMore": "Załaduj więcej", "common.changelog": "Nowości", "common.noReleasesFound": "Nie znaleziono wersji", + "common.time.justNow": "Przed chwilą", + "common.time.minutesAgo.short": "{{count}} min temu", + "common.time.hoursAgo.short": "{{count}} godz. temu", + "common.time.daysAgo.short": "{{count}} dni temu", "changelog.tag.latest": "Najnowszy", "common.key.esc": "ESC", "sidebar.menu.toggle": "Przełącz menu", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index 30b16b5b224..9d9c741bfb5 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -575,6 +575,10 @@ export const dict = { "common.loadMore": "Загрузить ещё", "common.changelog": "Что нового", "common.noReleasesFound": "Версии не найдены", + "common.time.justNow": "Только что", + "common.time.minutesAgo.short": "{{count}} мин назад", + "common.time.hoursAgo.short": "{{count}} ч назад", + "common.time.daysAgo.short": "{{count}} д назад", "changelog.tag.latest": "Последний", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 6dd0aa414ef..1c789c8aa44 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -569,6 +569,10 @@ export const dict = { "common.loadMore": "โหลดเพิ่มเติม", "common.changelog": "อัปเดต", "common.noReleasesFound": "ไม่พบเวอร์ชัน", + "common.time.justNow": "เมื่อสักครู่นี้", + "common.time.minutesAgo.short": "{{count}} นาทีที่แล้ว", + "common.time.hoursAgo.short": "{{count}} ชม. ที่แล้ว", + "common.time.daysAgo.short": "{{count}} วันที่แล้ว", "changelog.tag.latest": "ล่าสุด", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index d6e17510504..fbf3dda0a91 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -568,6 +568,10 @@ export const dict = { "common.loadMore": "加载更多", "common.changelog": "更新日志", "common.noReleasesFound": "未找到版本", + "common.time.justNow": "刚刚", + "common.time.minutesAgo.short": "{{count}}分钟前", + "common.time.hoursAgo.short": "{{count}}小时前", + "common.time.daysAgo.short": "{{count}}天前", "changelog.tag.latest": "最新", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 7209ff9f649..ef2eab6a940 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -565,6 +565,10 @@ export const dict = { "common.loadMore": "載入更多", "common.changelog": "更新日誌", "common.noReleasesFound": "未找到版本", + "common.time.justNow": "剛剛", + "common.time.minutesAgo.short": "{{count}}分鐘前", + "common.time.hoursAgo.short": "{{count}}小時前", + "common.time.daysAgo.short": "{{count}}天前", "changelog.tag.latest": "最新", "common.key.esc": "ESC", diff --git a/packages/app/src/utils/time.ts b/packages/app/src/utils/time.ts index ac709d86dd6..8df107282cb 100644 --- a/packages/app/src/utils/time.ts +++ b/packages/app/src/utils/time.ts @@ -1,4 +1,12 @@ -export function getRelativeTime(dateString: string): string { +type TimeKey = + | "common.time.justNow" + | "common.time.minutesAgo.short" + | "common.time.hoursAgo.short" + | "common.time.daysAgo.short" + +export type Translate = (key: TimeKey, params?: Record) => string + +export function getRelativeTime(dateString: string, t: Translate): string { const date = new Date(dateString) const now = new Date() const diffMs = now.getTime() - date.getTime() @@ -7,8 +15,8 @@ export function getRelativeTime(dateString: string): string { const diffHours = Math.floor(diffMinutes / 60) const diffDays = Math.floor(diffHours / 24) - if (diffSeconds < 60) return "Just now" - if (diffMinutes < 60) return `${diffMinutes}m ago` - if (diffHours < 24) return `${diffHours}h ago` - return `${diffDays}d ago` + if (diffSeconds < 60) return t("common.time.justNow") + if (diffMinutes < 60) return t("common.time.minutesAgo.short", { count: diffMinutes }) + if (diffHours < 24) return t("common.time.hoursAgo.short", { count: diffHours }) + return t("common.time.daysAgo.short", { count: diffDays }) } From 276d60e82a2097d24fc13d8829108446612775d6 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Sun, 1 Mar 2026 05:45:07 +0800 Subject: [PATCH 15/16] fix ar.ts, sync dialog-select-file.tsx to fix typecheck --- .../app/src/components/dialog-select-file.tsx | 4 +-- packages/app/src/i18n/ar.ts | 33 ++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index 29a3666c034..53ade67d85a 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -449,7 +449,7 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
- {getRelativeTime(new Date(item.updated!).toISOString())} + {getRelativeTime(new Date(item.updated!).toISOString(), language.t)}
@@ -459,4 +459,4 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
) -} +} \ No newline at end of file diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index d92e3fecda9..413fb54fd91 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -65,8 +65,8 @@ export const dict = { "command.model.variant.cycle.description": "التبديل إلى مستوى الجهد التالي", "command.prompt.mode.shell": "Shell", "command.prompt.mode.normal": "Prompt", - "command.permissions.autoaccept.enable": "قبول التعديلات تلقائيًا", - "command.permissions.autoaccept.disable": "إيقاف قبول التعديلات تلقائيًا", + "command.permissions.autoaccept.enable": "قبول الأذونات تلقائيًا", + "command.permissions.autoaccept.disable": "إيقاف قبول الأذونات تلقائيًا", "command.workspace.toggle": "تبديل مساحات العمل", "command.workspace.toggle.description": "تمكين أو تعطيل مساحات العمل المتعددة في الشريط الجانبي", "command.session.undo": "تراجع", @@ -366,10 +366,10 @@ export const dict = { "toast.workspace.enabled.description": "الآن يتم عرض عدة worktrees في الشريط الجانبي", "toast.workspace.disabled.title": "تم تعطيل مساحات العمل", "toast.workspace.disabled.description": "يتم عرض worktree الرئيسي فقط في الشريط الجانبي", - "toast.permissions.autoaccept.on.title": "قبول التعديلات تلقائيًا", - "toast.permissions.autoaccept.on.description": "سيتم الموافقة تلقائيًا على أذونات التحرير والكتابة", - "toast.permissions.autoaccept.off.title": "توقف قبول التعديلات تلقائيًا", - "toast.permissions.autoaccept.off.description": "ستتطلب أذونات التحرير والكتابة موافقة", + "toast.permissions.autoaccept.on.title": "يتم قبول الأذونات تلقائيًا", + "toast.permissions.autoaccept.on.description": "ستتم الموافقة على طلبات الأذونات تلقائيًا", + "toast.permissions.autoaccept.off.title": "تم إيقاف قبول الأذونات تلقائيًا", + "toast.permissions.autoaccept.off.description": "ستتطلب طلبات الأذونات موافقة", "toast.model.none.title": "لم يتم تحديد نموذج", "toast.model.none.description": "قم بتوصيل موفر لتلخيص هذه الجلسة", "toast.file.loadFailed.title": "فشل تحميل الملف", @@ -510,11 +510,6 @@ export const dict = { "common.noReleasesFound": "لم يتم العثور على إصدارات", "changelog.tag.latest": "الأحدث", - "common.time.justNow": "الآن", - "common.time.minutesAgo.short": "قبل {{count}} د", - "common.time.hoursAgo.short": "قبل {{count}} س", - "common.time.daysAgo.short": "قبل {{count}} ي", - "common.key.esc": "ESC", "sidebar.menu.toggle": "تبديل القائمة", "sidebar.nav.projectsAndSessions": "المشاريع والجلسات", @@ -743,4 +738,18 @@ export const dict = { "workspace.reset.archived.one": "ستتم أرشفة جلسة واحدة.", "workspace.reset.archived.many": "ستتم أرشفة {{count}} جلسات.", "workspace.reset.note": "سيؤدي هذا إلى إعادة تعيين مساحة العمل لتتطابق مع الفرع الافتراضي.", -} + "common.open": "فتح", + "dialog.releaseNotes.action.getStarted": "البدء", + "dialog.releaseNotes.action.next": "التالي", + "dialog.releaseNotes.action.hideFuture": "عدم إظهار هذا في المستقبل", + "dialog.releaseNotes.media.alt": "معاينة الإصدار", + "toast.project.reloadFailed.title": "فشل في إعادة تحميل {{project}}", + "error.server.invalidConfiguration": "تكوين غير صالح", + "common.moreCountSuffix": " (+{{count}} إضافي)", + "common.time.justNow": "الآن", + "common.time.minutesAgo.short": "قبل {{count}} د", + "common.time.hoursAgo.short": "قبل {{count}} س", + "common.time.daysAgo.short": "قبل {{count}} ي", + "settings.providers.connected.environmentDescription": "متصل من متغيرات البيئة الخاصة بك", + "settings.providers.custom.description": "أضف مزود متوافق مع OpenAI بواسطة عنوان URL الأساسي.", +} \ No newline at end of file From fe0f298293ff3322f8645c9db9d7c993cbeeedd5 Mon Sep 17 00:00:00 2001 From: Alex Yaroshuk Date: Sun, 1 Mar 2026 06:00:24 +0800 Subject: [PATCH 16/16] fix i18n --- packages/app/src/i18n/br.ts | 4 ---- packages/app/src/i18n/bs.ts | 4 ---- packages/app/src/i18n/da.ts | 4 ---- packages/app/src/i18n/de.ts | 4 ---- packages/app/src/i18n/en.ts | 5 ----- packages/app/src/i18n/es.ts | 4 ---- packages/app/src/i18n/fr.ts | 4 ---- packages/app/src/i18n/ja.ts | 4 ---- packages/app/src/i18n/ko.ts | 4 ---- packages/app/src/i18n/no.ts | 4 ---- packages/app/src/i18n/pl.ts | 4 ---- packages/app/src/i18n/ru.ts | 4 ---- packages/app/src/i18n/th.ts | 4 ---- packages/app/src/i18n/zh.ts | 4 ---- packages/app/src/i18n/zht.ts | 4 ---- 15 files changed, 61 deletions(-) diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 398c2f44d6f..c85555dbdc9 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -514,10 +514,6 @@ export const dict = { "common.loadMore": "Carregar mais", "common.changelog": "Novidades", "common.noReleasesFound": "Nenhuma release encontrada", - "common.time.justNow": "Agora mesmo", - "common.time.minutesAgo.short": "{{count}}m atrás", - "common.time.hoursAgo.short": "{{count}}h atrás", - "common.time.daysAgo.short": "{{count}}d atrás", "changelog.tag.latest": "Mais recente", "common.key.esc": "ESC", "sidebar.menu.toggle": "Alternar menu", diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index 628520cb3d4..b38b5ca3bfa 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -574,10 +574,6 @@ export const dict = { "common.loadMore": "Učitaj još", "common.changelog": "Novosti", "common.noReleasesFound": "Nema pronađenih verzija", - "common.time.justNow": "Upravo sada", - "common.time.minutesAgo.short": "prije {{count}} min", - "common.time.hoursAgo.short": "prije {{count}} h", - "common.time.daysAgo.short": "prije {{count}} d", "changelog.tag.latest": "Najnovije", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/da.ts b/packages/app/src/i18n/da.ts index 72eb35f0308..0e712c1e31d 100644 --- a/packages/app/src/i18n/da.ts +++ b/packages/app/src/i18n/da.ts @@ -570,10 +570,6 @@ export const dict = { "common.loadMore": "Indlæs flere", "common.changelog": "Nyheder", "common.noReleasesFound": "Ingen versioner fundet", - "common.time.justNow": "Lige nu", - "common.time.minutesAgo.short": "{{count}}m siden", - "common.time.hoursAgo.short": "{{count}}t siden", - "common.time.daysAgo.short": "{{count}}d siden", "changelog.tag.latest": "Seneste", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/de.ts b/packages/app/src/i18n/de.ts index 3fb77e66c2b..bc785851075 100644 --- a/packages/app/src/i18n/de.ts +++ b/packages/app/src/i18n/de.ts @@ -522,10 +522,6 @@ export const dict = { "common.loadMore": "Mehr laden", "common.changelog": "Neuerungen", "common.noReleasesFound": "Keine Versionen gefunden", - "common.time.justNow": "Gerade eben", - "common.time.minutesAgo.short": "vor {{count}} Min", - "common.time.hoursAgo.short": "vor {{count}} Std", - "common.time.daysAgo.short": "vor {{count}} Tg", "changelog.tag.latest": "Neueste", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index fcdeb877e19..897526f167f 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -598,11 +598,6 @@ export const dict = { "changelog.tag.latest": "Latest", "common.key.esc": "ESC", - "common.time.justNow": "Just now", - "common.time.minutesAgo.short": "{{count}}m ago", - "common.time.hoursAgo.short": "{{count}}h ago", - "common.time.daysAgo.short": "{{count}}d ago", - "sidebar.menu.toggle": "Toggle menu", "sidebar.nav.projectsAndSessions": "Projects and sessions", "sidebar.settings": "Settings", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 7c9f282da7a..ff6b8864b25 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -577,10 +577,6 @@ export const dict = { "common.loadMore": "Cargar más", "common.changelog": "Novedades", "common.noReleasesFound": "No se encontraron versiones", - "common.time.justNow": "Justo ahora", - "common.time.minutesAgo.short": "hace {{count}} min", - "common.time.hoursAgo.short": "hace {{count}} h", - "common.time.daysAgo.short": "hace {{count}} d", "changelog.tag.latest": "Último", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/fr.ts b/packages/app/src/i18n/fr.ts index 0ae7ae57abd..24ef3753bab 100644 --- a/packages/app/src/i18n/fr.ts +++ b/packages/app/src/i18n/fr.ts @@ -518,10 +518,6 @@ export const dict = { "common.loadMore": "Charger plus", "common.changelog": "Nouveautés", "common.noReleasesFound": "Aucune version trouvée", - "common.time.justNow": "À l'instant", - "common.time.minutesAgo.short": "il y a {{count}}m", - "common.time.hoursAgo.short": "il y a {{count}}h", - "common.time.daysAgo.short": "il y a {{count}}j", "changelog.tag.latest": "Dernier", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/ja.ts b/packages/app/src/i18n/ja.ts index 20161a95bc9..45567b2129e 100644 --- a/packages/app/src/i18n/ja.ts +++ b/packages/app/src/i18n/ja.ts @@ -512,10 +512,6 @@ export const dict = { "common.loadMore": "さらに読み込む", "common.changelog": "更新履歴", "common.noReleasesFound": "バージョンが見つかりません", - "common.time.justNow": "たった今", - "common.time.minutesAgo.short": "{{count}} 分前", - "common.time.hoursAgo.short": "{{count}} 時間前", - "common.time.daysAgo.short": "{{count}} 日前", "changelog.tag.latest": "最新", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/ko.ts b/packages/app/src/i18n/ko.ts index 9d3230d4004..66c77cbd8f8 100644 --- a/packages/app/src/i18n/ko.ts +++ b/packages/app/src/i18n/ko.ts @@ -513,10 +513,6 @@ export const dict = { "common.loadMore": "더 불러오기", "common.changelog": "새로운 기능", "common.noReleasesFound": "버전을 찾을 수 없음", - "common.time.justNow": "방금 전", - "common.time.minutesAgo.short": "{{count}}분 전", - "common.time.hoursAgo.short": "{{count}}시간 전", - "common.time.daysAgo.short": "{{count}}일 전", "changelog.tag.latest": "최신", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index f2c583a9d63..7fa10179c5e 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -577,10 +577,6 @@ export const dict = { "common.loadMore": "Last flere", "common.changelog": "Nyheter", "common.noReleasesFound": "Ingen versjoner funnet", - "common.time.justNow": "Akkurat nå", - "common.time.minutesAgo.short": "{{count}} m siden", - "common.time.hoursAgo.short": "{{count}} t siden", - "common.time.daysAgo.short": "{{count}} d siden", "changelog.tag.latest": "Siste", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index d129200ec4d..34981e92240 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -513,10 +513,6 @@ export const dict = { "common.loadMore": "Załaduj więcej", "common.changelog": "Nowości", "common.noReleasesFound": "Nie znaleziono wersji", - "common.time.justNow": "Przed chwilą", - "common.time.minutesAgo.short": "{{count}} min temu", - "common.time.hoursAgo.short": "{{count}} godz. temu", - "common.time.daysAgo.short": "{{count}} dni temu", "changelog.tag.latest": "Najnowszy", "common.key.esc": "ESC", "sidebar.menu.toggle": "Przełącz menu", diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index fa9fce661a7..d1c0f794ad4 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -575,10 +575,6 @@ export const dict = { "common.loadMore": "Загрузить ещё", "common.changelog": "Что нового", "common.noReleasesFound": "Версии не найдены", - "common.time.justNow": "Только что", - "common.time.minutesAgo.short": "{{count}} мин назад", - "common.time.hoursAgo.short": "{{count}} ч назад", - "common.time.daysAgo.short": "{{count}} д назад", "changelog.tag.latest": "Последний", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index 100705b8152..74f3b948039 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -569,10 +569,6 @@ export const dict = { "common.loadMore": "โหลดเพิ่มเติม", "common.changelog": "อัปเดต", "common.noReleasesFound": "ไม่พบเวอร์ชัน", - "common.time.justNow": "เมื่อสักครู่นี้", - "common.time.minutesAgo.short": "{{count}} นาทีที่แล้ว", - "common.time.hoursAgo.short": "{{count}} ชม. ที่แล้ว", - "common.time.daysAgo.short": "{{count}} วันที่แล้ว", "changelog.tag.latest": "ล่าสุด", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index 5e535455c7b..473569146dd 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -568,10 +568,6 @@ export const dict = { "common.loadMore": "加载更多", "common.changelog": "更新日志", "common.noReleasesFound": "未找到版本", - "common.time.justNow": "刚刚", - "common.time.minutesAgo.short": "{{count}}分钟前", - "common.time.hoursAgo.short": "{{count}}小时前", - "common.time.daysAgo.short": "{{count}}天前", "changelog.tag.latest": "最新", "common.key.esc": "ESC", diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 3b37c785798..0db7b0b38d6 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -565,10 +565,6 @@ export const dict = { "common.loadMore": "載入更多", "common.changelog": "更新日誌", "common.noReleasesFound": "未找到版本", - "common.time.justNow": "剛剛", - "common.time.minutesAgo.short": "{{count}}分鐘前", - "common.time.hoursAgo.short": "{{count}}小時前", - "common.time.daysAgo.short": "{{count}}天前", "changelog.tag.latest": "最新", "common.key.esc": "ESC",