Compare commits

7 Commits

9 changed files with 236 additions and 1 deletions
+66 -1
View File
@@ -1,2 +1,67 @@
# Hearth_Server # Hearth Server 示例
Hearth 启动器的 webAPP / 卡片下发服务端示例。**本质是纯静态文件**,无需后端逻辑。
## 目录结构
```
Hearth_Server/
├── manifest.json # webAPP 清单(客户端启动时拉取)
├── apps/<id>/index.html # 各 webAPP 入口页
├── icons/<id>.svg # 图标
├── offlines/<id>.zip # 可选离线包(整站资源打成 zip)
└── cards/
├── catalog.json # 首页卡片目录
└── <id>/card.html # 单个卡片
```
## 快速启动(测试用)
```bash
cd ~/Project/Hearth_Server
python3 -m http.server 8080
```
然后在 Hearth 设置页 →「webAPP 服务器地址」填 `http://<本机IP>:8080`
## 正式部署
- nginx`location / { root /path/to/Hearth_Server; }`
- 或传云对象存储(OSS/COS)+ CDN,整个目录原样上传
## 更新流程
1. 改 webAPP 资源(`apps/` 下的文件)
2.`manifest.json``version`+1
3. 客户端「检查更新」或重启即可拉取
## manifest.json 字段
| 字段 | 说明 |
|---|---|
| `version` | 清单版本号,客户端据此判断更新 |
| `apps[].id` | 唯一 id |
| `apps[].name` | 显示名 |
| `apps[].icon` | 图标地址(建议 SVG |
| `apps[].url` | 入口页地址(相对地址即可) |
| `apps[].offline.package` | 可选离线包 zip 路径 |
| `apps[].offline.version` | 离线包版本 |
## 离线包
- 把某个 webAPP 的**全部资源**`apps/<id>/` 下所有文件,含子目录)打成 zip,放 `offlines/<id>.zip`
- zip 内需**包含 index.html**(作为离线入口)
- 生成示例:`cd apps/demo && zip -r ../../offlines/demo.zip .`
## 离线包使用与版本管理
- **下载时机**:客户端拉取清单时**只同步列表,不自动下载程序包**;只有首次点开某 webAPP 时才下载(显示进度覆盖层)。
- **本地优先**:下载后解压到本地目录,之后点开直接本地加载;**服务端删除该离线包不影响本地运行**。
- **手动导入**:长按 webAPP 卡片 →「导入离线包」→ 选择本地 zip(适用于无服务器或离线分发场景)。
- **版本更新**
- `manifest.json` 里把 `offline.version` 升版本后,客户端列表该 webAPP 显示「**可更新**」标签(**不强制**,点开仍运行本地旧版)。
- 长按卡片弹出菜单:
- `更新离线包`(删除本地并重新下载,显示版本 x → y)
- `删除本地包`
- `属性`(查看本地 / 云端版本号)
- 云端版本号 ≤ 本地版本号时不提示更新。
+45
View File
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>网易云音乐</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #1a1a1a; }
#frame {
position: absolute;
top: 0; left: 0;
width: 1280px; /* 网易云 PC 版参考宽度 */
height: 720px; /* 参考高度(16:9 */
transform-origin: 0 0;
border: none;
}
</style>
</head>
<body>
<iframe id="frame" src="https://music.163.com/st/webplayer" allow="autoplay; encrypted-media"></iframe>
<script>
// 动态缩放:使网易云 PC 版 UI 适配 Hearth 的内容区(横屏)
// Dynamically scale the NetEase PC UI to fit Hearth's landscape content area
(function () {
var FRAME_W = 1280;
var FRAME_H = 720;
function fit() {
var frame = document.getElementById('frame');
var w = window.innerWidth;
var h = window.innerHeight;
var scale = Math.min(w / FRAME_W, h / FRAME_H);
frame.style.transform = 'scale(' + scale + ')';
// 缩放后居中
var sw = FRAME_W * scale;
var sh = FRAME_H * scale;
frame.style.left = ((w - sw) / 2) + 'px';
frame.style.top = ((h - sh) / 2) + 'px';
}
window.addEventListener('resize', fit);
fit();
})();
</script>
</body>
</html>
+57
View File
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>示例应用</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, "MiSans", "PingFang SC", sans-serif;
background: #f5f5f7;
color: #1a1a1a;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
padding: 24px;
}
.icon {
width: 72px; height: 72px; border-radius: 20px;
background: linear-gradient(135deg, #ff6900, #ff9d00);
display: flex; align-items: center; justify-content: center;
font-size: 34px; color: #fff;
}
h1 { font-size: 24px; font-weight: 600; }
.card {
background: #fff; border-radius: 16px; padding: 16px 20px;
width: 100%; max-width: 360px;
box-shadow: 0 2px 12px rgba(0,0,0,.06);
}
.row { display: flex; justify-content: space-between; padding: 8px 0; font-size: 14px; }
.row .k { color: #8a8a90; }
.note { font-size: 12px; color: #8a8a90; text-align: center; line-height: 1.6; }
</style>
</head>
<body>
<div class="icon">🎨</div>
<h1>示例应用</h1>
<div class="card">
<div class="row"><span class="k">来源</span><span>远程服务器</span></div>
<div class="row"><span class="k">当前时间</span><span id="clock">--:--:--</span></div>
</div>
<p class="note">这是 Hearth 服务端的示例 webapp<br>用于演示远程下发 / 加载进度 / 离线包</p>
<script>
function tick() {
var now = new Date();
var p = function (n) { return String(n).padStart(2, '0'); };
document.getElementById('clock').textContent =
p(now.getHours()) + ':' + p(now.getMinutes()) + ':' + p(now.getSeconds());
}
tick();
setInterval(tick, 1000);
</script>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
{
"cards": [
{
"id": "weather",
"name": "天气",
"priority": 2,
"entry": "cards/weather/card.html"
}
]
}
+24
View File
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
/* 卡片是 HTML 片段,Hearth 会把它塞进 .card 容器 */
/* 卡片内容:天气示例 */
* { margin: 0; padding: 0; box-sizing: border-box; }
.w { display: flex; align-items: center; gap: 10px; font-family: sans-serif; }
.w .ic { font-size: 28px; }
.w .t { font-size: 20px; font-weight: 600; color: inherit; }
.w .s { font-size: 12px; color: #8a8a90; }
</style>
</head>
<body>
<div class="w">
<div class="ic"></div>
<div>
<div class="t">26°</div>
<div class="s"></div>
</div>
</div>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<rect width="24" height="24" rx="6" fill="#c62f2f"/>
<path d="M9 18V6l10-2v11" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="6.5" cy="18" r="2.2" fill="#fff"/>
<circle cx="16.5" cy="15" r="2.2" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 342 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<rect width="24" height="24" rx="6" fill="#ff6900"/>
<text x="12" y="17" font-size="13" text-anchor="middle" fill="white" font-family="sans-serif">H</text>
</svg>

After

Width:  |  Height:  |  Size: 228 B

+24
View File
@@ -0,0 +1,24 @@
{
"version": 1,
"updatedAt": 1723785600,
"apps": [
{
"id": "demo",
"name": "示例应用",
"icon": "icons/demo.svg",
"url": "apps/demo/index.html",
"offline": {
"package": "offlines/demo.zip",
"version": "1.0.0"
}
},
{
"id": "cloudmusic",
"name": "网易云音乐",
"icon": "icons/cloudmusic.svg",
"url": "https://music.163.com/st/webplayer",
"ua": "desktop",
"scale": 65
}
]
}
BIN
View File
Binary file not shown.