add website downloads and deploy workflow (#1165)
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
name: Website
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- base
|
||||
paths:
|
||||
- packages/website/**
|
||||
- packages/client/src/styles/variables.scss
|
||||
- package.json
|
||||
- package-lock.json
|
||||
- tsconfig.website.json
|
||||
- vite.config.website.ts
|
||||
- .github/workflows/website-deploy.yml
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build website
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
- name: Type-check website
|
||||
run: npx vue-tsc -p tsconfig.website.json --noEmit
|
||||
|
||||
- name: Build website
|
||||
run: npm run build:website
|
||||
|
||||
- name: Prepare SSH
|
||||
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
WEBSITE_SSH_KEY: ${{ secrets.WEBSITE_SSH_KEY }}
|
||||
WEBSITE_SSH_KNOWN_HOSTS: ${{ secrets.WEBSITE_SSH_KNOWN_HOSTS }}
|
||||
run: |
|
||||
test -n "$WEBSITE_SSH_KEY"
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
printf '%s\n' "$WEBSITE_SSH_KEY" > ~/.ssh/website_deploy_key
|
||||
chmod 600 ~/.ssh/website_deploy_key
|
||||
if [ -n "$WEBSITE_SSH_KNOWN_HOSTS" ]; then
|
||||
printf '%s\n' "$WEBSITE_SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||
fi
|
||||
|
||||
- name: Deploy website
|
||||
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
WEBSITE_SSH_USER: ${{ secrets.WEBSITE_SSH_USER }}
|
||||
WEBSITE_SSH_PORT: ${{ secrets.WEBSITE_SSH_PORT }}
|
||||
run: |
|
||||
SSH_USER="${WEBSITE_SSH_USER:-root}"
|
||||
SSH_PORT="${WEBSITE_SSH_PORT:-22}"
|
||||
command -v rsync >/dev/null || {
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y rsync
|
||||
}
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ~/.ssh/website_deploy_key -p ${SSH_PORT} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new" \
|
||||
dist/website/ \
|
||||
"$SSH_USER@154.3.33.232:/var/www/ekkolearnai.com/current/"
|
||||
@@ -4,12 +4,14 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
A full-featured web dashboard for <a href="https://github.com/NousResearch/hermes-agent">Hermes Agent</a>.<br/>
|
||||
A full-featured desktop app and web dashboard for <a href="https://github.com/NousResearch/hermes-agent">Hermes Agent</a>.<br/>
|
||||
Manage AI chat sessions, monitor usage & costs, configure platform channels,<br/>
|
||||
schedule cron jobs, browse skills — all from a clean, responsive web interface.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/EKKOLearnAI/hermes-web-ui/releases/latest">Download Hermes Studio Desktop</a>
|
||||
·
|
||||
<code>npm install -g hermes-web-ui && hermes-web-ui start</code>
|
||||
</p>
|
||||
|
||||
@@ -171,7 +173,22 @@ hermes-web-ui reset-default-login
|
||||
|
||||
## Quick Start
|
||||
|
||||
### npm (Recommended)
|
||||
### Desktop App (Recommended)
|
||||
|
||||
Download the latest **Hermes Studio** desktop installer from
|
||||
[GitHub Releases](https://github.com/EKKOLearnAI/hermes-web-ui/releases/latest).
|
||||
|
||||
Desktop builds are published for macOS, Windows, and Linux, with separate
|
||||
architecture assets where applicable. The desktop app bundles the Web UI
|
||||
runtime and stores Hermes Agent data in the native Hermes location:
|
||||
|
||||
- Windows: `%LOCALAPPDATA%\hermes` (falls back to `%APPDATA%\hermes`)
|
||||
- macOS/Linux: `~/.hermes`
|
||||
|
||||
The desktop wrapper stores its own Web UI state separately in
|
||||
`~/.hermes-web-ui` unless `HERMES_WEB_UI_HOME` is set.
|
||||
|
||||
### npm
|
||||
|
||||
```bash
|
||||
npm install -g hermes-web-ui
|
||||
|
||||
+18
-2
@@ -4,12 +4,14 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/NousResearch/hermes-agent">Hermes Agent</a> 的全功能 Web 管理面板。<br/>
|
||||
<a href="https://github.com/NousResearch/hermes-agent">Hermes Agent</a> 的全功能桌面应用和 Web 管理面板。<br/>
|
||||
管理 AI 聊天会话、监控用量与成本、配置平台渠道、<br/>
|
||||
管理定时任务、浏览技能 —— 全部在一个简洁响应式的 Web 界面中完成。
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/EKKOLearnAI/hermes-web-ui/releases/latest">下载 Hermes Studio 桌面版</a>
|
||||
·
|
||||
<code>npm install -g hermes-web-ui && hermes-web-ui start</code>
|
||||
</p>
|
||||
|
||||
@@ -179,7 +181,21 @@ hermes-web-ui reset-default-login
|
||||
|
||||
## 快速开始
|
||||
|
||||
### npm 安装(推荐)
|
||||
### 桌面应用(推荐)
|
||||
|
||||
从 [GitHub Releases](https://github.com/EKKOLearnAI/hermes-web-ui/releases/latest)
|
||||
下载最新的 **Hermes Studio** 桌面安装包。
|
||||
|
||||
桌面版会发布 macOS、Windows 和 Linux 构建;适用时会区分不同 CPU 架构。
|
||||
桌面应用内置 Web UI 运行时,Hermes Agent 数据会保存到原生 Hermes 目录:
|
||||
|
||||
- Windows:`%LOCALAPPDATA%\hermes`(找不到时回退到 `%APPDATA%\hermes`)
|
||||
- macOS/Linux:`~/.hermes`
|
||||
|
||||
桌面壳自身的 Web UI 状态会单独保存到 `~/.hermes-web-ui`,除非设置了
|
||||
`HERMES_WEB_UI_HOME`。
|
||||
|
||||
### npm 安装
|
||||
|
||||
```bash
|
||||
npm install -g hermes-web-ui
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
|
||||
Electron desktop distribution for Hermes Studio.
|
||||
|
||||
## Install
|
||||
|
||||
Download the latest macOS, Windows, or Linux installer for your CPU
|
||||
architecture from the project
|
||||
[GitHub Releases](https://github.com/EKKOLearnAI/hermes-web-ui/releases/latest).
|
||||
|
||||
The desktop app bundles the Web UI runtime and launches it locally from the
|
||||
native shell app.
|
||||
|
||||
## Data directories
|
||||
|
||||
Hermes Agent data is stored in the same platform-specific location as native
|
||||
|
||||
@@ -150,7 +150,7 @@ onMounted(() => {
|
||||
<div class="install-box animate-fade-in animate-delay-3">
|
||||
<code>{{ installCmd }}</code>
|
||||
<button class="copy-btn" @click="copyCmd">
|
||||
{{ copied ? 'Copied!' : 'Copy' }}
|
||||
{{ copied ? t('ui.copied') : t('ui.copy') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useScrollReveal } from '@/composables/useScrollReveal'
|
||||
|
||||
const { t } = useI18n()
|
||||
interface DesktopDownload {
|
||||
title: string
|
||||
desc: string
|
||||
assetSuffix: string
|
||||
}
|
||||
|
||||
const { t, tm } = useI18n()
|
||||
useScrollReveal()
|
||||
const activeTab = ref<'npm' | 'docker' | 'source'>('npm')
|
||||
const activeTab = ref<'desktop' | 'npm' | 'docker' | 'source'>('desktop')
|
||||
|
||||
const releaseVersion = __APP_VERSION__.replace(/^v/, '')
|
||||
const releaseTag = `v${releaseVersion}`
|
||||
const releaseBaseUrl = 'https://github.com/EKKOLearnAI/hermes-web-ui/releases'
|
||||
const releaseUrl = `${releaseBaseUrl}/tag/${releaseTag}`
|
||||
const releaseDownloadUrl = `${releaseBaseUrl}/download/${releaseTag}`
|
||||
const desktopDownloads = computed(() =>
|
||||
(tm('install.desktop.downloads') as DesktopDownload[]).map((item) => ({
|
||||
...item,
|
||||
href: `${releaseDownloadUrl}/Hermes.Studio-${releaseVersion}-${item.assetSuffix}`,
|
||||
})),
|
||||
)
|
||||
|
||||
function copyText(text: string) {
|
||||
navigator.clipboard.writeText(text).catch(() => {})
|
||||
@@ -19,7 +37,7 @@ function copyText(text: string) {
|
||||
|
||||
<div class="install-tabs reveal">
|
||||
<button
|
||||
v-for="tab in (['npm', 'docker', 'source'] as const)"
|
||||
v-for="tab in (['desktop', 'npm', 'docker', 'source'] as const)"
|
||||
:key="tab"
|
||||
class="tab-btn"
|
||||
:class="{ active: activeTab === tab }"
|
||||
@@ -30,7 +48,33 @@ function copyText(text: string) {
|
||||
</div>
|
||||
|
||||
<div class="install-content reveal reveal-delay-1">
|
||||
<template v-if="activeTab === 'npm'">
|
||||
<template v-if="activeTab === 'desktop'">
|
||||
<div class="download-list">
|
||||
<a
|
||||
v-for="item in desktopDownloads"
|
||||
:key="item.href"
|
||||
class="download-row"
|
||||
:href="item.href"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<span>
|
||||
<strong>{{ item.title }}</strong>
|
||||
<small>{{ item.desc }}</small>
|
||||
</span>
|
||||
<span class="download-action">{{ t('install.desktop.download') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
class="all-downloads"
|
||||
:href="releaseUrl"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{{ t('install.desktop.allDownloads') }}
|
||||
</a>
|
||||
</template>
|
||||
<template v-else-if="activeTab === 'npm'">
|
||||
<div class="code-block" @click="copyText(t('install.npm.cmd1'))">
|
||||
<code>{{ t('install.npm.cmd1') }}</code>
|
||||
</div>
|
||||
@@ -51,7 +95,7 @@ function copyText(text: string) {
|
||||
<code>{{ t('install.source.cmd2') }}</code>
|
||||
</div>
|
||||
</template>
|
||||
<p class="prereq">{{ t('install.prereq') }}</p>
|
||||
<p class="prereq">{{ activeTab === 'desktop' ? t('install.desktop.prereq') : t('install.prereq') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -88,6 +132,8 @@ function copyText(text: string) {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: $radius-md;
|
||||
padding: 4px;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
@@ -114,6 +160,65 @@ function copyText(text: string) {
|
||||
// full width within panel
|
||||
}
|
||||
|
||||
.download-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.download-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&:hover .download-action {
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
strong,
|
||||
small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 15px;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.download-action {
|
||||
flex: 0 0 auto;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: $radius-sm;
|
||||
padding: 7px 12px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: border-color $transition-fast;
|
||||
}
|
||||
|
||||
.all-downloads {
|
||||
display: inline-flex;
|
||||
margin-top: 14px;
|
||||
color: var(--text-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useScrollReveal } from '@/composables/useScrollReveal'
|
||||
|
||||
interface ScreenshotItem {
|
||||
src: string
|
||||
alt: string
|
||||
}
|
||||
|
||||
const { t, tm } = useI18n()
|
||||
useScrollReveal()
|
||||
|
||||
const images = [
|
||||
{ src: '/image1.png', alt: 'AI Chat with Image Generation' },
|
||||
{ src: '/image2.png', alt: 'Chat and File Browser' },
|
||||
{ src: '/image3.png', alt: 'Multi-panel Workspace' },
|
||||
{ src: '/image4.png', alt: 'Kanban Board' },
|
||||
]
|
||||
|
||||
const images = computed(() => tm('screenshots.items') as ScreenshotItem[])
|
||||
const activeIndex = ref(0)
|
||||
let timer: ReturnType<typeof setInterval>
|
||||
|
||||
function next() {
|
||||
activeIndex.value = (activeIndex.value + 1) % images.length
|
||||
activeIndex.value = (activeIndex.value + 1) % images.value.length
|
||||
}
|
||||
|
||||
function prev() {
|
||||
activeIndex.value = (activeIndex.value - 1 + images.length) % images.length
|
||||
activeIndex.value = (activeIndex.value - 1 + images.value.length) % images.value.length
|
||||
}
|
||||
|
||||
function setActive(i: number) {
|
||||
@@ -53,7 +54,7 @@ onUnmounted(() => {
|
||||
<span class="dot green" />
|
||||
</div>
|
||||
<div class="browser-url">
|
||||
<span>http://localhost:8648</span>
|
||||
<span>{{ t('screenshots.localUrl') }}</span>
|
||||
</div>
|
||||
<div class="browser-spacer" />
|
||||
</div>
|
||||
@@ -71,7 +72,7 @@ onUnmounted(() => {
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="screenshot-nav">
|
||||
<button class="nav-arrow" @click="prev(); resetTimer()">
|
||||
<button class="nav-arrow" :aria-label="t('screenshots.previous')" @click="prev(); resetTimer()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6" /></svg>
|
||||
</button>
|
||||
|
||||
@@ -80,12 +81,13 @@ onUnmounted(() => {
|
||||
v-for="(_img, i) in images"
|
||||
:key="i"
|
||||
class="dot-btn"
|
||||
:aria-label="t('screenshots.goTo', { number: i + 1 })"
|
||||
:class="{ active: activeIndex === i }"
|
||||
@click="setActive(i)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button class="nav-arrow" @click="next(); resetTimer()">
|
||||
<button class="nav-arrow" :aria-label="t('screenshots.next')" @click="next(); resetTimer()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -39,19 +39,19 @@ onMounted(async () => {
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" class="star-icon">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
<span>Star</span>
|
||||
<span>{{ t('starHistory.star') }}</span>
|
||||
<span v-if="stars !== null" class="star-count">{{ stars.toLocaleString() }}</span>
|
||||
</a>
|
||||
|
||||
<img
|
||||
class="github-badge"
|
||||
src="https://img.shields.io/github/license/EKKOLearnAI/hermes-web-ui?style=flat-square"
|
||||
alt="License"
|
||||
:alt="t('starHistory.licenseAlt')"
|
||||
/>
|
||||
<img
|
||||
class="github-badge"
|
||||
src="https://img.shields.io/github/v/release/EKKOLearnAI/hermes-web-ui?style=flat-square"
|
||||
alt="Version"
|
||||
:alt="t('starHistory.versionAlt')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,7 @@ onMounted(async () => {
|
||||
>
|
||||
<img
|
||||
:src="chartSrc"
|
||||
alt="Star History"
|
||||
:alt="t('starHistory.chartAlt')"
|
||||
class="chart-img"
|
||||
/>
|
||||
</a>
|
||||
|
||||
@@ -9,8 +9,8 @@ const { t } = useI18n()
|
||||
<div class="footer-inner">
|
||||
<div class="footer-left">
|
||||
<div class="footer-brand">
|
||||
<img src="/logo.png" alt="Hermes" class="footer-logo" />
|
||||
<span>Hermes Web UI</span>
|
||||
<img src="/logo.png" :alt="t('brand.logoAlt')" class="footer-logo" />
|
||||
<span>{{ t('brand.name') }}</span>
|
||||
</div>
|
||||
<p class="footer-desc">{{ t('footer.description') }}</p>
|
||||
</div>
|
||||
|
||||
@@ -30,8 +30,8 @@ function goHome() {
|
||||
<header class="site-header">
|
||||
<div class="header-inner">
|
||||
<div class="header-left" @click="goHome">
|
||||
<img src="/logo.png" alt="Hermes" class="logo-icon" />
|
||||
<span class="logo-text">Hermes Web UI</span>
|
||||
<img src="/logo.png" :alt="t('brand.logoAlt')" class="logo-icon" />
|
||||
<span class="logo-text">{{ t('brand.name') }}</span>
|
||||
</div>
|
||||
|
||||
<nav class="header-nav">
|
||||
@@ -50,10 +50,10 @@ function goHome() {
|
||||
<line x1="10" y1="14" x2="21" y2="3" />
|
||||
</svg>
|
||||
</a>
|
||||
<button class="icon-btn" @click="switchLocale" :title="locale === 'en' ? '中文' : 'English'">
|
||||
<button class="icon-btn" @click="switchLocale" :title="locale === 'en' ? t('ui.switchToChinese') : t('ui.switchToEnglish')">
|
||||
{{ locale === 'en' ? '中' : 'EN' }}
|
||||
</button>
|
||||
<button class="icon-btn" @click="toggleTheme" :title="isDark ? 'Light' : 'Dark'">
|
||||
<button class="icon-btn" @click="toggleTheme" :title="isDark ? t('ui.lightTheme') : t('ui.darkTheme')">
|
||||
<svg v-if="isDark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
@@ -71,7 +71,7 @@ function goHome() {
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<button class="mobile-toggle" @click="mobileMenuOpen = !mobileMenuOpen">
|
||||
<button class="mobile-toggle" @click="mobileMenuOpen = !mobileMenuOpen" :title="t('ui.menu')">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
@@ -92,7 +92,7 @@ function goHome() {
|
||||
<line x1="2" y1="12" x2="22" y2="12" />
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||
</svg>
|
||||
{{ locale === 'en' ? '中文' : 'English' }}
|
||||
{{ locale === 'en' ? t('ui.switchToChinese') : t('ui.switchToEnglish') }}
|
||||
</button>
|
||||
<button class="mobile-action-btn" @click="toggleTheme">
|
||||
<svg v-if="isDark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="action-icon">
|
||||
@@ -109,7 +109,7 @@ function goHome() {
|
||||
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="action-icon">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
||||
</svg>
|
||||
{{ isDark ? 'Light Mode' : 'Dark Mode' }}
|
||||
{{ isDark ? t('ui.lightMode') : t('ui.darkMode') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
export default {
|
||||
brand: {
|
||||
name: 'Hermes Web UI',
|
||||
logoAlt: 'Hermes',
|
||||
},
|
||||
ui: {
|
||||
copy: 'Copy',
|
||||
copied: 'Copied!',
|
||||
darkTheme: 'Dark',
|
||||
lightTheme: 'Light',
|
||||
darkMode: 'Dark Mode',
|
||||
lightMode: 'Light Mode',
|
||||
menu: 'Menu',
|
||||
switchToChinese: 'Chinese',
|
||||
switchToEnglish: 'English',
|
||||
},
|
||||
nav: {
|
||||
home: 'Home',
|
||||
docs: 'Documentation',
|
||||
@@ -75,9 +90,59 @@ export default {
|
||||
wechat: 'WeChat',
|
||||
wecom: 'WeCom',
|
||||
},
|
||||
screenshots: {
|
||||
localUrl: 'http://localhost:8648',
|
||||
previous: 'Previous screenshot',
|
||||
next: 'Next screenshot',
|
||||
goTo: 'View screenshot {number}',
|
||||
items: [
|
||||
{ src: '/image1.png', alt: 'AI chat with image generation' },
|
||||
{ src: '/image2.png', alt: 'Chat and file browser' },
|
||||
{ src: '/image3.png', alt: 'Multi-panel workspace' },
|
||||
{ src: '/image4.png', alt: 'Kanban board' },
|
||||
],
|
||||
},
|
||||
install: {
|
||||
title: 'Quick Start',
|
||||
desc: 'Get Hermes Web UI running in under a minute.',
|
||||
desc: 'Download the desktop app or run Hermes Web UI yourself.',
|
||||
desktop: {
|
||||
title: 'Desktop',
|
||||
download: 'Download',
|
||||
allDownloads: 'View all release assets',
|
||||
prereq: 'Desktop builds bundle the Web UI runtime.',
|
||||
downloads: [
|
||||
{
|
||||
title: 'macOS Apple Silicon',
|
||||
desc: 'Apple Silicon DMG',
|
||||
assetSuffix: 'arm64.dmg',
|
||||
},
|
||||
{
|
||||
title: 'macOS Intel',
|
||||
desc: 'x64 DMG',
|
||||
assetSuffix: 'x64.dmg',
|
||||
},
|
||||
{
|
||||
title: 'Windows',
|
||||
desc: 'x64 installer',
|
||||
assetSuffix: 'x64.exe',
|
||||
},
|
||||
{
|
||||
title: 'Linux x64 AppImage',
|
||||
desc: 'x64 AppImage',
|
||||
assetSuffix: 'x86_64.AppImage',
|
||||
},
|
||||
{
|
||||
title: 'Linux x64 Debian',
|
||||
desc: 'amd64 .deb package',
|
||||
assetSuffix: 'amd64.deb',
|
||||
},
|
||||
{
|
||||
title: 'Linux arm64',
|
||||
desc: 'arm64 AppImage',
|
||||
assetSuffix: 'arm64.AppImage',
|
||||
},
|
||||
],
|
||||
},
|
||||
npm: {
|
||||
title: 'npm',
|
||||
cmd1: 'npm install -g hermes-web-ui',
|
||||
@@ -97,6 +162,10 @@ export default {
|
||||
starHistory: {
|
||||
title: 'Growing Community',
|
||||
desc: 'Star us on GitHub and join the community.',
|
||||
star: 'Star',
|
||||
licenseAlt: 'License',
|
||||
versionAlt: 'Version',
|
||||
chartAlt: 'Star History',
|
||||
},
|
||||
footer: {
|
||||
description: 'Self-hosted AI chat dashboard for Hermes Agent.',
|
||||
@@ -104,6 +173,7 @@ export default {
|
||||
madeWith: 'Built with Vue 3, Naive UI, and TypeScript.',
|
||||
},
|
||||
docs: {
|
||||
placeholder: 'Select a section from the sidebar to get started.',
|
||||
sidebar: {
|
||||
gettingStarted: 'Getting Started',
|
||||
configuration: 'Configuration',
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
export default {
|
||||
brand: {
|
||||
name: 'Hermes Web UI',
|
||||
logoAlt: 'Hermes',
|
||||
},
|
||||
ui: {
|
||||
copy: '复制',
|
||||
copied: '已复制',
|
||||
darkTheme: '深色',
|
||||
lightTheme: '浅色',
|
||||
darkMode: '深色模式',
|
||||
lightMode: '浅色模式',
|
||||
menu: '菜单',
|
||||
switchToChinese: '中文',
|
||||
switchToEnglish: 'English',
|
||||
},
|
||||
nav: {
|
||||
home: '首页',
|
||||
docs: '文档',
|
||||
@@ -75,9 +90,59 @@ export default {
|
||||
wechat: '微信',
|
||||
wecom: '企业微信',
|
||||
},
|
||||
screenshots: {
|
||||
localUrl: 'http://localhost:8648',
|
||||
previous: '上一张截图',
|
||||
next: '下一张截图',
|
||||
goTo: '查看第 {number} 张截图',
|
||||
items: [
|
||||
{ src: '/image1.png', alt: '带图片生成的 AI 聊天界面' },
|
||||
{ src: '/image2.png', alt: '聊天和文件浏览器界面' },
|
||||
{ src: '/image3.png', alt: '多面板工作区界面' },
|
||||
{ src: '/image4.png', alt: '看板管理界面' },
|
||||
],
|
||||
},
|
||||
install: {
|
||||
title: '快速开始',
|
||||
desc: '一分钟内启动 Hermes Web UI。',
|
||||
desc: '下载桌面应用,或自行运行 Hermes Web UI。',
|
||||
desktop: {
|
||||
title: '桌面版',
|
||||
download: '下载',
|
||||
allDownloads: '查看全部发布文件',
|
||||
prereq: '桌面版已内置 Web UI 运行时。',
|
||||
downloads: [
|
||||
{
|
||||
title: 'macOS Apple Silicon',
|
||||
desc: 'Apple Silicon DMG',
|
||||
assetSuffix: 'arm64.dmg',
|
||||
},
|
||||
{
|
||||
title: 'macOS Intel',
|
||||
desc: 'x64 DMG',
|
||||
assetSuffix: 'x64.dmg',
|
||||
},
|
||||
{
|
||||
title: 'Windows',
|
||||
desc: 'x64 安装包',
|
||||
assetSuffix: 'x64.exe',
|
||||
},
|
||||
{
|
||||
title: 'Linux x64 AppImage',
|
||||
desc: 'x64 AppImage',
|
||||
assetSuffix: 'x86_64.AppImage',
|
||||
},
|
||||
{
|
||||
title: 'Linux x64 Debian',
|
||||
desc: 'amd64 .deb 安装包',
|
||||
assetSuffix: 'amd64.deb',
|
||||
},
|
||||
{
|
||||
title: 'Linux arm64',
|
||||
desc: 'arm64 AppImage',
|
||||
assetSuffix: 'arm64.AppImage',
|
||||
},
|
||||
],
|
||||
},
|
||||
npm: {
|
||||
title: 'npm',
|
||||
cmd1: 'npm install -g hermes-web-ui',
|
||||
@@ -97,6 +162,10 @@ export default {
|
||||
starHistory: {
|
||||
title: '社区成长',
|
||||
desc: '在 GitHub 上给我们加星,加入社区。',
|
||||
star: '加星',
|
||||
licenseAlt: '许可证',
|
||||
versionAlt: '版本',
|
||||
chartAlt: 'Star 历史',
|
||||
},
|
||||
footer: {
|
||||
description: 'Hermes Agent 的自托管 AI 聊天仪表板。',
|
||||
@@ -104,6 +173,7 @@ export default {
|
||||
madeWith: '使用 Vue 3、Naive UI 和 TypeScript 构建。',
|
||||
},
|
||||
docs: {
|
||||
placeholder: '从侧边栏选择一个章节开始阅读。',
|
||||
sidebar: {
|
||||
gettingStarted: '快速开始',
|
||||
configuration: '配置说明',
|
||||
|
||||
@@ -39,7 +39,7 @@ function navigate(name: string) {
|
||||
<router-view />
|
||||
<DocContent v-if="route.meta.page" />
|
||||
<div v-else class="docs-placeholder">
|
||||
<p>Select a section from the sidebar to get started.</p>
|
||||
<p>{{ t('docs.placeholder') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,16 +9,28 @@ import StarHistorySection from '@/components/landing/StarHistorySection.vue'
|
||||
<template>
|
||||
<div class="landing">
|
||||
<HeroSection />
|
||||
<section class="download-section">
|
||||
<InstallSection />
|
||||
</section>
|
||||
<ScreenshotsSection />
|
||||
<FeaturesGrid />
|
||||
<div class="cta-row">
|
||||
<InstallSection class="cta-col" />
|
||||
<StarHistorySection class="cta-col" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.download-section {
|
||||
max-width: 1120px;
|
||||
margin: 0 auto;
|
||||
padding: 56px 24px 16px;
|
||||
|
||||
@media (max-width: $breakpoint-mobile) {
|
||||
padding: 32px 16px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-row {
|
||||
max-width: 1120px;
|
||||
margin: 0 auto;
|
||||
|
||||
Reference in New Issue
Block a user