仓库是什么
Clink 从公开的 GitHub 仓库读取包。你按名称添加一个,Clink 读它最新的发行版,每个文件都会在安装前完成校验。没有任何内容被上传,你的东西也不会在服务器上运行。
请输入 owner/repository、github.com/owner/repository 或完整的 HTTPS URL。在下载任何内容之前,Clink 都会验证每个发布清单和文件。
先挑一种
每一种包都有自己的文件扩展名和自己在清单里的集合名,Clink 也分别给它们各自的大小上限。挑好你要发布的那一行,并照着来:一个被归到面板底下的主题,根本读不出来。
| 种类 | 文件 | 集合 | 单个文件上限(字节) | 官方仓库 |
|---|---|---|---|---|
| 主题 | .clinktheme | themes | 128,000 | anti-ltd/clink-themes |
| 布局 | .clinklayout | layouts | 512,000 | anti-ltd/clink-layouts |
| 配置文件 | .clinkprofile | profiles | 256,000 | anti-ltd/clink-profiles |
| 声音 | .clinkpack | sounds | 512,000 | anti-ltd/clink-sounds |
| 字体 | .otf .ttf | fonts | 32,000,000 | anti-ltd/clink-fonts |
| 面板 | .clinkpanel | panels | 50,000 | anti-ltd/clink-panels |
| 操作 | .clinkext | actions | 48,000 | anti-ltd/clink-actions |
| 插件 | .clinkplugin | plugins | 66,000 | anti-ltd/clink-plugins |
语言包是例外。那里的一个包是一组词库和模型文件,而不是一个文档,官方仓库里带着构建它们的整套工具。请从那边的 README 开始,而不是这一页。
从官方仓库起步
最快的路子是分叉。包目录、清单生成脚本和发布工作流都已经接好,还附带能直接照抄的可用示例。
# Everything a repository needs is already wired together in the official one.
gh repo fork anti-ltd/clink-panels --clone
cd clink-panels
# Your packs replace the examples. Keep tools/ and .github/.
rm Panels/*.clinkpanel
cp ~/Downloads/my-panel.clinkpanel Panels/kaomoji-plus.clinkpanel保留 tools/ 和 .github/workflows/。正是这两样把一次推送变成 Clink 读得懂的发行版;缺了它们的分叉什么也发布不出来。
或者用三个文件从头搭
仓库并不神秘。它需要一个放包的目录、一个写清单的脚本,以及一个切发行版的工作流。
my-panels/
├── Panels/
│ └── kaomoji.clinkpanel # one file per pack, the name is the id
├── tools/
│ └── build-manifest.py # writes manifest.json from that folder
└── .github/workflows/
└── release.yml # turns a push into a release工作流做三件事:重建清单、删掉上一个发行版、再发布一个标记为 latest 的新发行版,把包文件和清单一并附上。就这些。
name: Release panels
on:
push:
branches: [main]
paths: ["Panels/**", "tools/**", ".github/workflows/release.yml"]
permissions: { contents: write }
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python3 tools/build-manifest.py
- run: gh release delete latest --yes || true
env: { GH_TOKEN: "${{ github.token }}" }
- run: gh release create latest Panels/*.clinkpanel manifest.json --title "Latest panels" --latest
env: { GH_TOKEN: "${{ github.token }}" }生成脚本遍历目录,为每个文件写一条记录。哈希和字节数只从这里产生,所以它跑在工作流里,而不是靠手动执行。
#!/usr/bin/env python3
import hashlib, json, os, pathlib
root = pathlib.Path(__file__).resolve().parents[1]
repository = os.environ.get("GITHUB_REPOSITORY", "<owner>/<repository>")
packs = []
for path in sorted((root / "Panels").glob("*.clinkpanel")):
data = path.read_bytes()
pack = json.loads(data)
packs.append({
"id": path.stem,
"name": pack["name"],
"version": "latest",
"asset": {
"path": path.name,
"url": f"https://github.com/{repository}/releases/download/latest/{path.name}",
"sha256": hashlib.sha256(data).hexdigest(),
"byteCount": len(data),
},
})
(root / "manifest.json").write_text(json.dumps({"version": "latest", "panels": packs}, indent=2))清单
每个发行版都会把 manifest.json 与包文件一起发布,Clink 从最新发行版的 https://github.com/<owner>/<repository>/releases/latest/download/manifest.json 读取它。每条记录写明文件名、它在同一发行版中的地址、SHA-256 哈希和精确的字节数。
{
"version": "latest",
"panels": [
{
"id": "kaomoji",
"name": "Kaomoji",
"version": "latest",
"asset": {
"path": "kaomoji.clinkpanel",
"url": "https://github.com/<owner>/<repository>/releases/download/latest/kaomoji.clinkpanel",
"sha256": "8f14e45fce…",
"byteCount": 2048
}
}
]
}集合名必须和种类对上。面板放在 panels 下,主题放在 themes 下,上面那张表依此类推。
发布前先试一遍
仓库里的包,就是你能直接打开的那个文件,所以先走捷径试:用隔空投送发到手机上,或者放进“文件”再用 Clink 打开。之后再检查脚本写出的清单。
python3 tools/build-manifest.py
cat manifest.json- 运行生成脚本。它会按目录里此刻的内容重写 manifest.json。
- 读一遍它写出的东西。每条记录都应指向一个真实存在的文件,字节数也要对得上。
- 在 Clink 里打开包文件本身用一用。面板或操作会落进各自的编辑器,可以就地运行。
发布
git add Panels manifest.json
git commit -m "Add my first panel"
git push推送到 main,剩下的交给工作流。它是替换 latest 发行版而不是往上追加,所以最新一次推送永远就是别人拿到的东西。
仓库发布版本后,再打开此标签页。
在 Clink 里添加
- 打开“通用”,进入“仓库”,添加 owner/repository。
- 打开你这一类的标签页并下拉刷新。你的包会按清单里给的名字列出来。
- 点一个开始下载。Clink 校验之后,像装其他包一样装上。
数据包只需添加仓库即可:主题、布局、配置文件、声音、字体和语言。面板、动作和插件带有逻辑,所以每个仓库都需要单独的开关,Clink 才会安装其中任何一种,应用会在第一次时询问。
面板包含受约束的交互逻辑。只允许你信任的仓库。
Clink 会检查什么
- 地址是 github.com 上的 HTTPS,且位于你添加的那个仓库的发行版之下。
- 下载的字节数与清单完全一致,64 位的 SHA-256 哈希也对得上。
- 文件带着正确的扩展名,并且能按它自称的包类型解析。
- 面板、动作和插件在存储前都要通过源代码策略。
下载被拒绝时,无非这五种原因。应用里的提示故意写得很短,所以按顺序排查。
| 检查项 | 拒绝条件 |
|---|---|
github.com | 地址不是 github.com 上的 HTTPS,或者不在你添加的那个仓库的发行版之下。清单不能指向别处,哪怕是你自己的另一个仓库。 |
byteCount | 文件大小和清单声明的不完全一致,或者超过了该种类的上限。每次改完就重建清单,能解决其中绝大多数。 |
sha256 | 哈希和字节对不上。通常是清单写在文件最后一次改动之前。 |
path | 文件名没有以该种类要求的扩展名结尾,或者用两个点试图爬出发行版。 |
source | 某个面板、动作或插件未通过源代码策略:过长,或含有策略拒绝的片段。 |
更新,以及收回
id 就是去掉扩展名的文件名,它决定了一次发布算不算更新。保持文件名不变,新发行版就会替换掉别人手上的那一份。改了名字,你就是在第一个旁边又发布了第二个包。
把包从仓库里撤下来只会停掉新的下载。它触及不到已经装好的手机,所以一个糟糕的发行版应该去替换,而不是删掉。
官方仓库
分叉其中任意一个就能开始。每个仓库都会生成自己的清单,并用 GitHub Actions 自行发布。