feat: add release notes pages
This commit is contained in:
parent
56ca81d245
commit
fc35df65b8
202
resources/cherry-studio/releases.html
Normal file
202
resources/cherry-studio/releases.html
Normal file
@ -0,0 +1,202 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Github Releases Timeline</title>
|
||||
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/typography@0.5.10/dist/typography.min.css"></script>
|
||||
</head>
|
||||
|
||||
<body id="app">
|
||||
<div :class="isDark ? 'dark-bg' : 'bg'" class="min-h-screen">
|
||||
<div class="max-w-3xl mx-auto py-12 px-4">
|
||||
<h1 class="text-3xl font-bold mb-8" :class="isDark ? 'text-white' : 'text-gray-900'">Release Timeline</h1>
|
||||
|
||||
<!-- Loading状态 -->
|
||||
<div v-if="loading" class="text-center py-8">
|
||||
<div class="inline-block animate-spin rounded-full h-8 w-8 border-4"
|
||||
:class="isDark ? 'border-gray-700 border-t-blue-500' : 'border-gray-300 border-t-blue-500'"></div>
|
||||
</div>
|
||||
|
||||
<!-- Error 状态 -->
|
||||
<div v-else-if="error" class="text-red-500 text-center py-8">{{ error }}</div>
|
||||
|
||||
<!-- Release 列表 -->
|
||||
<div v-else class="space-y-8">
|
||||
<div v-for="release in releases" :key="release.id" class="relative pl-8"
|
||||
:class="isDark ? 'border-l-2 border-gray-700' : 'border-l-2 border-gray-200'">
|
||||
<div class="absolute -left-2 top-0 w-4 h-4 rounded-full bg-green-500"></div>
|
||||
<div class="rounded-lg shadow-sm p-6 transition-shadow"
|
||||
:class="isDark ? 'bg-black hover:shadow-md hover:shadow-black' : 'bg-white hover:shadow-md'">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold" :class="isDark ? 'text-white' : 'text-gray-900'">
|
||||
{{ release.name || release.tag_name }}
|
||||
</h2>
|
||||
<p class="text-sm mt-1" :class="isDark ? 'text-gray-400' : 'text-gray-500'">
|
||||
{{ formatDate(release.published_at) }}
|
||||
</p>
|
||||
</div>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium"
|
||||
:class="isDark ? 'bg-green-900 text-green-200' : 'bg-green-100 text-green-800'">
|
||||
{{ release.tag_name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="prose" :class="isDark ? 'text-gray-300 dark-prose' : 'text-gray-600'"
|
||||
v-html="renderMarkdown(release.body)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const md = window.markdownit({
|
||||
breaks: true,
|
||||
linkify: true
|
||||
})
|
||||
|
||||
const { createApp } = Vue
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
releases: [],
|
||||
loading: true,
|
||||
error: null,
|
||||
isDark: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchReleases() {
|
||||
try {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
const response = await fetch('https://api.github.com/repos/kangfenmao/cherry-studio/releases')
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch releases')
|
||||
}
|
||||
this.releases = await response.json()
|
||||
} catch (err) {
|
||||
this.error = 'Error loading releases: ' + err.message
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
formatDate(dateString) {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
},
|
||||
renderMarkdown(content) {
|
||||
if (!content) return ''
|
||||
return md.render(content)
|
||||
},
|
||||
initTheme() {
|
||||
// 从 URL 参数获取主题设置
|
||||
const url = new URL(window.location.href)
|
||||
const theme = url.searchParams.get('theme')
|
||||
this.isDark = theme === 'dark'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initTheme()
|
||||
this.fetchReleases()
|
||||
}
|
||||
}).mount('#app')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 基础的 Markdown 样式 */
|
||||
.prose {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.prose h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.prose h2 {
|
||||
font-size: 1.3em;
|
||||
margin: 0.8em 0;
|
||||
}
|
||||
|
||||
.prose h3 {
|
||||
font-size: 1.1em;
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.prose ul {
|
||||
list-style-type: disc;
|
||||
margin-left: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.prose ol {
|
||||
list-style-type: decimal;
|
||||
margin-left: 1.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.prose code {
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 0.2em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.dark .prose code {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.prose code {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.prose pre code {
|
||||
display: block;
|
||||
padding: 1em;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.prose a {
|
||||
color: #3b82f6;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.dark .prose a {
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.prose blockquote {
|
||||
border-left: 4px solid #e5e7eb;
|
||||
padding-left: 1em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.dark .prose blockquote {
|
||||
border-left-color: #374151;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.dark .prose {
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.dark-bg {
|
||||
background-color: #151515;
|
||||
}
|
||||
|
||||
.bg {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -10,6 +10,7 @@ import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { setUpdateState } from '@renderer/store/runtime'
|
||||
import { setManualUpdateCheck } from '@renderer/store/settings'
|
||||
import { ThemeMode } from '@renderer/types'
|
||||
import { compareVersions, runAsyncFunction } from '@renderer/utils'
|
||||
import { Avatar, Button, Progress, Row, Switch, Tag } from 'antd'
|
||||
import { debounce } from 'lodash'
|
||||
@ -71,6 +72,15 @@ const AboutSettings: FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const showReleases = async () => {
|
||||
const { appPath } = await window.api.getAppInfo()
|
||||
MinApp.start({
|
||||
name: t('settings.about.releases.title'),
|
||||
url: `file://${appPath}/resources/cherry-studio/releases.html?theme=${theme === ThemeMode.dark ? 'dark' : 'light'}`,
|
||||
logo: AppLogo
|
||||
})
|
||||
}
|
||||
|
||||
const hasNewVersion = update?.info?.version && version ? compareVersions(update.info.version, version) > 0 : false
|
||||
|
||||
useEffect(() => {
|
||||
@ -158,16 +168,7 @@ const AboutSettings: FC = () => {
|
||||
<SoundOutlined />
|
||||
{t('settings.about.releases.title')}
|
||||
</SettingRowTitle>
|
||||
<Button
|
||||
onClick={() =>
|
||||
MinApp.start({
|
||||
name: t('settings.about.releases.title'),
|
||||
url: 'https://github.com/kangfenmao/cherry-studio/releases',
|
||||
logo: AppLogo
|
||||
})
|
||||
}>
|
||||
{t('settings.about.releases.button')}
|
||||
</Button>
|
||||
<Button onClick={showReleases}>{t('settings.about.releases.button')}</Button>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user