社区皮肤与皮肤包
任何人都可以把皮肤免费分享给所有 FolderSkin 用户。一组分享出来的皮肤叫作皮肤包,只分享一款皮肤时,它就是只装了一款的皮肤包。皮肤包存放在单独的仓库 folderskin-community 的 packs/ 下,添加皮肤包不需要账号。FolderSkin 本身不带任何皮肤:所有皮肤都来自皮肤包、你自己的图片和 AI 生成的结果。
FolderSkin 第一次打开时,会推荐一些皮肤包,帮你把皮肤库先充实起来。之后可以在应用里打开社区。顶部的筛选项就是各个皮肤包的标签。添加会把皮肤包里的皮肤放进你的皮肤库,并带上皮肤包的标签,每款皮肤的 ⋯ 菜单里都会注明它来自哪个皮肤包、由谁分享。移除会把整个皮肤包再拿掉。已经用上其中某款皮肤的文件夹会保留现有的图标,因为图标就保存在文件夹本身里。
从文件夹添加对你电脑上的皮肤包文件夹做同样的事,分享之前也可以用它先试试你的皮肤包。
folderskin.app 的皮肤包画廊里,每个皮肤包都有一个安装按钮。点它会打开 FolderSkin,在社区中定位到这个皮肤包并添加,效果和点它的添加按钮一样(见下文的安装链接)。标有官方的皮肤包由维护者亲自把关。
皮肤包要么完整添加,要么完全不加:先下载并检查每一张图片,再一次性全部保存,所以即使断了网或者磁盘满了,你的皮肤库里也不会只留下半个皮肤包。其中的皮肤按皮肤包自身的顺序排列。
分享你的皮肤
Section titled “分享你的皮肤”直接在应用里分享即可,FolderSkin 会把皮肤包发送到它的社区服务 community.folderskin.app,由维护者审核。你不需要 GitHub 账号。FolderSkin 0.1.6 及更早的版本还可以替你在 GitHub 上发起 pull request,0.1.7 去掉了这个选项。
- 给想分享的皮肤打上标签(⋯ → 标签)。分享单款皮肤,用 ⋯ → 分享到社区。一次分享多款,用社区 → 分享你的皮肤,然后选一个标签。
- 填写皮肤包的名称、标签和许可协议,说明图片的来源,并勾选确认这些图片你有权分享。
- 第一次分享时,FolderSkin 会在浏览器里验证这台电脑,并登记你的皮肤包署名所用的名字。每台电脑只需验证一次。
- 提交审核。在任何数据离开你的电脑之前,FolderSkin 会先按下文的格式规范检查皮肤包。我的投稿里会列出你提交过的每个皮肤包,如果有被拒绝的,也会说明原因。
每张图片都以无损方式分享,所以皮肤包看起来和你做的一模一样,连成品文件夹的透明边缘也不例外。发送之前,FolderSkin 会把每张图片转成无损 WebP,每张需要几秒钟,界面会实时显示已经转好了几张。如果一张图片细节太多,在 1024 px 下超过 1.5 MB,就会先缩到 896 px,还不行再缩到 768 px,依然无损,FolderSkin 也会告诉你是哪几张。一个皮肤包的图片加起来最多 64 MB,超出的会被拒绝,并建议你拆成两个皮肤包。
每个皮肤包都会先由真人审核,之后其他人才能看到。审核通过后,它会在大约 15 分钟内自动发布(见下文的审核通过后如何发布)。多个皮肤包可以同名:大家看到的是你起的名字,而皮肤包会有一个专属的 ID(见皮肤包 ID)。
皮肤包也可以手动提交:向 folderskin-community 发起一个 pull request,在 packs/ 下添加一个文件夹。用 packs make 生成这个文件夹(见用图片制作皮肤包),它会自动分配一个 ID,pull request 也会运行和应用里相同的检查。应用里的保存为文件夹生成的皮肤包文件夹符合下文的所有规则。
皮肤包 ID
Section titled “皮肤包 ID”每个皮肤包都有一个 ID,它就是皮肤包文件夹的名字,也是所有指向它的链接的一部分。ID 在创建皮肤包时生成一次,由名称加上六个随机字符组成:名为 Classic Art 的皮肤包,ID 可能是 classic-art-k7q2mx。名称可以随意重复,就算有一百个皮肤包都叫 Classic Art 也没关系,只有 ID 必须唯一。手动制作的皮肤包由 packs make 生成 ID,从应用分享的皮肤包则由社区服务生成。ID 一经生成就不会再变,即使皮肤包改了名也一样。
随机部分是从 a 到 z、2 到 7 中取的六个字符,来自系统的安全随机源。新 ID 绝不会与 packs/ 中已有的文件夹同名,也不会与 moved.json 中的旧 ID 重复。
moved.json
Section titled “moved.json”在自动生成 ID 之前做的皮肤包,ID 只由名称构成,比如 classic-art。这些皮肤包已经用 packs rename 换上了自动生成的 ID,与 packs/ 同级的 moved.json 记录了每个旧 ID 以及对应皮肤包现在的 ID:
{ "version": 1, "moved": { "classic-art": "classic-art-k7q2mx" } }- 每个旧 ID 都不对应
packs/中的任何文件夹,也永远不会分配给新的皮肤包。 - 每个新 ID 都对应
packs/中的一个皮肤包,而且不会再指向另一个旧 ID:皮肤包再次改名时,所有原本指向它的记录都会改为指向它最新的 ID。 packs index和packs catalog会把这份映射以"moved"字段写入index.json和head.json,这样应用、网站和社区服务都能从旧 ID 找到对应的皮肤包。从 0.1.7 开始,应用会把你以旧 ID 添加的皮肤包迁移到新 ID 上,带旧 ID 的安装链接也仍然能找到对应的皮肤包。- 改名后的皮肤包保留首次发布的日期:
packs index以添加过它任一 ID 的最早那次提交作为发布日期。 pack.json里不写任何相关内容:它不接受格式规范之外的字段,多写一个字段,0.1.4 到 0.1.6 版的应用就会拒绝这个皮肤包。
packs rename
Section titled “packs rename”cargo run -p folderskin-tools -- packs rename --dir ../folderskin-community --allcargo run -p folderskin-tools -- packs rename --dir ../folderskin-community classic-artcargo run -p folderskin-tools -- packs rename --dir ../folderskin-community classic-art --to classic-art-k7q2mx它在 folderskin-community 的 git 本地仓库中为皮肤包换上自动生成的 ID。每个文件夹都用 git mv 移动,历史记录也随之保留。featured.json 和 official.json 会按原有顺序改写为新 ID,moved.json 会记录每一次迁移(文件不存在时会自动创建)。所有改动都会暂存好,可以直接提交。
--all 会重命名所有 ID 不是自动生成的皮肤包,其余的保持不变。它只看 ID 的格式:如果旧 ID 的最后一个词恰好是六个字母,比如 data-structures-in-bricks,看起来就像是自动生成的,这种皮肤包需要按 ID 单独重命名。单独指定一个皮肤包时,无论它现在的 ID 是什么,都会换成一个新生成的 ID,或者换成 --to 指定的 ID(它必须是自动生成格式的 ID,而且现在和过去都没有皮肤包用过)。重复运行不会有任何改动:已经迁移过的皮肤包能在 moved.json 中查到,会提示它已经迁移过了。
审核通过后如何发布
Section titled “审核通过后如何发布”皮肤包一经审核通过就会发布,不需要任何人手动复制文件。
- 维护者审核通过一个皮肤包后,社区服务会为它分配 ID。如果服务配置了 GitHub 令牌(
GITHUB_DISPATCH_TOKEN),还会立即通过pack-approvedrepository dispatch 启动 folderskin-community 的 Packs 工作流。 - 没有令牌时,工作流会自己找到这个皮肤包。它每 15 分钟向
https://community.folderskin.app/v1/exports/pending查询有多少已通过审核的皮肤包在等待发布,这只需几秒钟,只有确实有待发布的皮肤包时才会执行后续步骤。此外,不管有没有皮肤包在等待,它每天和每周都会各运行一次,维护者也可以随时手动启动它。 community pull --no-done把每个已通过审核的皮肤包写入packs/,并将每个文件的大小和 SHA-256 与上传时服务记录的值逐一核对。服务分配的 ID 必须是自动生成的,已经存在的文件夹绝不会被覆盖,也不会被加上编号另存一份。检查之前,皮肤包里的成品文件夹会先统一成同一种形状(见一个皮肤包,一种文件夹形状)。与这个形状相差太多的文件夹会保持原样,运行日志里会给出一条警告。packs check检查每个皮肤包,和检查 pull request 时一样。- 每个皮肤包都由 github-actions[bot] 提交,提交信息为
Add the <name> pack,并推送到main。 - 只有到这一步,
community done才会通知服务该皮肤包已发布。如果检查或推送失败,皮肤包会继续在服务端等待,下一次运行时再重试。 - 同一次运行还会重建
index.json、预览图和v2/,把v2/复制到镜像(见下文),然后一并提交。用工作流自带令牌进行的推送不会触发其他工作流,所以这些步骤都在同一次运行里完成。
因此,皮肤包会在审核通过后大约 15 分钟内发布,如果由服务直接启动工作流,只需几分钟。运行失败时不会发布任何内容,GitHub 会发邮件告诉维护者工作流失败了。GitHub 繁忙时,定时运行可能会晚一些才启动,而且仓库连续 60 天没有活动时,GitHub 会关掉定时任务。dispatch 方式则不受这两点影响。
工作流需要维护者的签名密钥,也就是 community keygen 生成的完整文件,保存为仓库 secret FOLDERSKIN_ADMIN_KEY。没有它,已通过审核的皮肤包会一直在服务端等待,运行日志也会说明这一点。把仓库变量 REQUIRE_GENERATED_IDS 设为 true 后,每次检查都会拒绝 ID 不是自动生成的皮肤包。
手动拉取仍然可行。community pull 写入皮肤包后会立即通知服务,因为运行它的人会自己提交写入的内容。--no-done --pulled pulled.json 会推迟通知,直到运行 community done --from pulled.json 为止,工作流就是这样运行的。重复通知服务也不会有任何问题。
应用可以从 https://packs.folderskin.app 读取整个目录树,这是一个 Cloudflare R2 存储桶,里面的 v2/ 与仓库中的完全相同。写入存储桶的是社区服务,所以工作流不需要自己的 Cloudflare 令牌:community mirror 会用维护者的密钥签名,通过服务上传每个文件。
cargo run -p folderskin-tools -- community mirror --tree ../folderskin-community \ --public https://packs.folderskin.app --api https://community.folderskin.app --key ~/folderskin-admin.key它用 HEAD 请求向镜像逐一查询每个文件,镜像上已有同样长度的文件就跳过:除了 head.json,每个文件的名字都是其内容的哈希。其余文件通过 PUT /v1/admin/tree/<path> 上传,每个请求都在 X-Content-SHA256 中附上文件的 SHA-256,供存储桶校验。head.json 最后上传,而且要等其他所有文件都到位之后才上传,所以镜像绝不会指向一个它自己并没有的目录。遇到可能自行恢复的失败(没有响应、5xx 或 429)时,请求总共会尝试五次,等待时间从一秒开始逐次翻倍,Retry-After 只要不超过 30 秒都会遵守。工作流会在提交之前运行它,所以只有当镜像已经拥有 head.json 所指向的全部内容后,GitHub 上的 head.json 才会更新。仓库变量 COMMUNITY_MIRROR_URL 用来开启镜像,只有在开启期间,head.json 才会列出它。
一个皮肤包就是一个文件夹:
packs/night-prints-h4x2qe/ pack.json koi.webp fox-in-the-rain.webppack.json:
{ "version": 1, "name": "Night prints", "author": "your-github-name", "license": "CC0-1.0", "tags": ["woodblock", "night"], "skins": [ { "file": "koi.webp", "name": "Koi over the wave", "tags": ["animals"] }, { "file": "fox-in-the-rain.webp", "name": "Fox in the rain" } ]}| 字段 | 规则 |
|---|---|
version | 1 |
name | 1 到 40 个字符 |
author | 你的 GitHub 用户名 |
license | CC0-1.0、CC-BY-4.0 或 MIT |
tags | 1 到 5 个标签。皮肤包里的每款皮肤都会带上这些标签,第一个标签就是这个皮肤包在所有人筛选项里显示的名字 |
skins | 1 到 50 项 |
skins[].file | 文件夹中的一张图片 |
skins[].name | 1 到 60 个字符 |
skins[].tags | 可选,为这款皮肤再加最多 3 个标签 |
不允许出现其他字段,所以像 "tag" 这样的拼写错误会让检查失败,而不是被悄悄忽略。
| 限制 | |
|---|---|
| 每个皮肤包的皮肤数量 | 1 到 50 |
| 每张图片 | 无损:PNG 或无损 WebP。最大 1.5 MB |
| 一个皮肤包的图片总和 | 最多 64 MB |
| 图片边长 | 256 到 1024 px |
| 文件名 | 字母、数字、.、- 和 _,以 .png 或 .webp 结尾 |
| ID,即文件夹名 | 名称加六个随机字符,比如 night-prints-h4x2qe(见皮肤包 ID):由小写字母和数字组成的单词,用单个连字符连接,最多 40 个字符 |
| 标签 | 小写字母、数字、空格和连字符,最多 24 个字符 |
pack.json | 最大 64 KB |
为什么是 50 款和 64 MB:皮肤包是一组有主题的皮肤,每个添加它的人都要下载全部内容。五十款皮肤审核起来依然很快,64 MB 足够放下五十张每张 1.3 MB 的图片,或者四十二张最大尺寸的图片。
FolderSkin 0.1.7 之前发布的皮肤包,要求是每张图片不超过 2 MB,格式可以是 PNG、JPEG 或任意 WebP,应用仍然可以读取它们。用 packs make 重新制作后,它们就会符合上面的规则。加上 --require-lossless 后,packs check 会按这些规则检查皮肤包(见自己检查皮肤包),社区服务也只接受符合这些规则的皮肤包。
每张图片属于以下两类之一,判断方式和拖进窗口的图片相同:
- 成品文件夹:画在透明背景上,或者纯品红
#FF00FF背景上(FolderSkin 会把品红抠掉)。它会原样成为图标。 - 其他任何图片都会被包裹到 FolderSkin 的文件夹上。SKINS.md 说明了文件夹会裁掉图片的哪些部分,帮你保住画面主体。
三个系统绘制的图标最大也就 1024 px,所以再大的图片也不会带来任何提升。
每张图片都是无损的,所以皮肤包看起来和制作时一模一样:渐变里没有色块,文字周围没有振铃伪影,成品文件夹的边缘也和画出来时一样干净。无损 WebP 比同样的 PNG 小约三分之一,所以应用和 packs make 都输出 WebP。一张细节丰富的 1024 px 图片大小在 0.6 到 1.5 MB 之间,大多在 800 KB 左右。超过 1.5 MB 的会先缩到 896 px,还不行再缩到 768 px,依然无损,而不是为了塞进限制把图片弄糊。
一个皮肤包,一种文件夹形状
Section titled “一个皮肤包,一种文件夹形状”FolderSkin 会把成品文件夹的整张图片完整放进图标里。一张一张生成的文件夹,都是紧贴着各自的边缘裁出来的,没有哪两张渲染图的比例完全一样:某个皮肤包里的文件夹,宽度是高度的 1.03 到 1.30 倍不等。在 Finder 里并排摆放时,矮胖的看起来比瘦高的小。所以同一个皮肤包里的成品文件夹会统一成同一种形状:
- 皮肤包的形状取所有文件夹形状的中位数。测量文件夹时,只算图片中不透明度超过一半的部分,按宽 ÷ 高计算,所以柔和的边缘或淡淡的阴影不算在内。
- 每个文件夹都按这个形状精确重绘。先裁到文件夹本身,再缩放到 962 px 宽(这正是 FolderSkin 自己的文件夹在 1024 px 模板中的宽度,见
folderskin-tools template),高度则由形状决定。然后把它放进一张透明的 1024 × 1024 图片,摆在模板文件夹所在的位置:左边缘对齐,站在同一条基线上。最后保存为无损 WebP。PNG 会变成同名的.webp,pack.json也会随之更新。 - 每个文件夹最多只调整 8%。这个幅度谁也看不出来。需要调整更多的文件夹属于“离群项”:拉伸到那个程度,文字和人脸都会显得被压扁,所以它的形状永远不会被改动。怎么处理取决于所用的命令,最终由人来决定。
- 图案保持不变。FolderSkin 会把它包裹到自己的文件夹上,所以它本身没有需要统一的形状。只有一个成品文件夹的皮肤包也同样不做处理。
packs make 会对它制作的每个皮肤包做这一步,community pull 会对它拉取的每个皮肤包做,packs normalize 则处理 packs/ 中已有的皮肤包。重复执行不会有任何变化:已经符合皮肤包形状、位置也正确的文件夹不会被重绘,内容已经一致的文件也不会被重写。
| 命令 | 对离群项的处理 |
|---|---|
packs make | 从皮肤包中剔除,并列出来。加上 --keep-outliers 则原样保留 |
packs normalize | 列出来,保持原样。加上 --drop-outliers 会把它从 pack.json 中移除,并删除对应的图片 |
community pull | 原样保留,并在输出中给出警告,Packs 工作流的日志里可以看到。别人分享的皮肤,绝不会在没有人拍板的情况下被丢弃 |
cargo run -p folderskin-tools -- packs normalize --dir ../folderskin-communitycargo run -p folderskin-tools -- packs normalize --dir ../folderskin-community classic-art-5rxas2 --tolerance 0.3cargo run -p folderskin-tools -- packs normalize --dir ../folderskin-community dreamscapes-ppfia6 --drop-outlierspacks normalize 会处理所有皮肤包,或者只处理指定的几个。对每个皮肤包,它会报告形状和重绘了哪些文件夹,并列出每个离群项及其偏差。没有通过 packs check 的皮肤包会先跳过,等它通过了再处理。--tolerance 用来修改 8% 这个上限,适用于离群项也应该统一形状的皮肤包,比如 Classic Art 里的《蒙娜丽莎》。--drop-outliers 仅供维护者使用:除此之外,没有任何操作会删除皮肤。packs check --require-one-shape 会拒绝文件夹之间形状相差超过 1% 的皮肤包(见自己检查皮肤包),按统一形状重绘过的文件夹则永远不会超出这个范围。
分享的皮肤使用 Creative Commons 或 MIT 许可协议:
CC0-1.0:任何人都可以用于任何用途。这是默认选项,因为大多数皮肤由 AI 生成,而 CC0 对它们主张的权利最少。CC-BY-4.0:任何人都可以使用,但要注明你是作者。MIT:任何人都可以使用,并且要保留你的名字。
只分享你自己做的、或者你有权分享的图片。
用图片制作皮肤包
Section titled “用图片制作皮肤包”packs make 可以把一个装满图片的文件夹(比如从图像模型保存下来的渲染图)变成一个皮肤包,放在本地 folderskin-community 仓库的 packs/ 下,而且直接就能通过检查。下面的命令在本仓库中运行,folderskin-community 克隆在它旁边:
cargo run -p folderskin-tools -- packs make ~/Downloads/3d-renders --dir ../folderskin-community \ --name "3D" --tags 3d,glossy --author your-github-name --preview /tmp/3d.png皮肤包会得到一个专属 ID,由名称加六个随机字符组成,比如 3d-k7q2mx,这也是它的文件夹名。运行报告里会给出这个 ID。它绝不会与 packs/ 中已有的文件夹重名,也不会与 moved.json 里的旧 ID 重复。
每张图片的分类方式和你在应用里添加图片时一样。成品文件夹(按 PROMPTS.md 里聊天提示词的要求画在品红背景上,或者带真正的透明背景)会被抠出来,直接成为图标。其他图片则作为图案,包裹到 FolderSkin 的文件夹上。每张图片都会缩小到 1024 px 并保存为无损 WebP,所用的编码器和应用分享皮肤包时用的是同一个,已经内置,不用另外安装。如果仍然超过 1.5 MB,就先缩到 896 px,还不行再缩到 768 px,运行报告里会注明。用 --max-kb 可以把图片限制得比 1.5 MB 更小。所有图片加起来超过 64 MB 时会被拒绝,并建议你拆成两个皮肤包。libwebp 最彻底的压缩设置每张图片要花好几秒,所以会同时用上所有 CPU 核心。关于格式的更多说明,见 SKINS.md。运行报告会说明每张图片被归为哪一类。皮肤以文件名命名,所以请先给文件起好名字,或者之后在 pack.json 里修改。--preview 会把每款皮肤都画成文件夹的样子,汇总在一张 PNG 里,方便你检查。
有两个或更多成品文件夹时,会统一成同一种形状(见一个皮肤包,一种文件夹形状),报告里会列出重绘了哪些。与其他文件夹形状相差超过 8% 的文件夹会被剔除,报告里会写明偏差多少。如果想原样保留它,就加上 --keep-outliers。没有保留任何离群项的皮肤包可以通过 packs check --require-one-shape。
--id 可以用新图片重新制作一个已有的皮肤包:--id 3d-k7q2mx 会替换 packs/3d-k7q2mx/ 中的所有内容,皮肤包保留原来的 ID,所以已经添加过它的人都会收到新版本的更新。新文件夹会先在别处生成并检查,通过后才会替换旧文件夹。不加 --id 时,packs make 总是创建一个新的皮肤包。
图像模型被要求画 #FF00FF 时,常常会画成均匀的覆盆子红或亮粉色(Grok 在制作 Classic Art 皮肤包时就是这样)。--flat-backdrop 可以抠掉任意颜色的纯色背景:它会测量每张图片自己的背景色,只去除与图片边缘相连的部分(所以文件夹里的红斗篷会保留下来),顺带去掉柔和的投影,并让边缘呈现画作本身的颜色,而不是一圈粉边。在纯灰或纯黑背景上,它只在背景自身的噪点范围内抠除,也绝不会向上蔓延,所以贴着文件夹边缘的深色外套或墨线不会被误认为背景。处理完后看一下 --preview 生成的预览图(它画在浅灰背景上,要是抠出了洞,一眼就能看出来)。没有纯色背景的图片仍然会被当作图案处理。
想看某张图片在应用里的效果,可以用 render 把它画成文件夹:
cargo run -p folderskin-tools -- render ../folderskin-community/packs/3d-k7q2mx/glass.webp --out /tmp/glass.png --size 512自己检查皮肤包
Section titled “自己检查皮肤包”在本仓库中运行,folderskin-community 克隆在它旁边:
cargo run -p folderskin-tools -- packs check --dir ../folderskin-community它用应用本身的规则检查 packs/ 中的每个文件夹,并用一句话描述每个问题。如果在 folderskin-community 的本地仓库里运行,可以省略 --dir:工具默认在当前文件夹中查找。它按 0.1.7 之前发布的皮肤包所遵循的标准,限制每张图片不超过 2 MB,每个皮肤包的图片加起来不超过 64 MB。--require-lossless 会按新皮肤包的规则检查每张图片:PNG 或无损 WebP,最大 1.5 MB,这也正是社区服务接受、packs make 输出的格式。在旧皮肤包重新制作完成之前,这个选项默认关闭,需要时再开启。--max-kb 可以把图片限制得更小。
它还会检查 ID 之间有没有冲突:两个文件夹的名字如果只有大小写不同,在 macOS 和 Windows 上就是同一个文件夹。皮肤包也不能使用 moved.json 中的旧 ID,而 moved.json 本身也要符合它自己的规则(见 moved.json)。名称不参与比较,因为名称可以重复。--require-generated-ids 会拒绝 ID 不是自动生成的皮肤包。它默认关闭,当 folderskin-community 的仓库变量 REQUIRE_GENERATED_IDS 为 true 时,工作流会开启它。
--require-one-shape 会拒绝成品文件夹形状不统一的皮肤包,也就是其中有两个文件夹的宽 ÷ 高相差超过 1%。按统一形状重绘过的文件夹总在这个范围内,所以它能揪出从来没统一过形状的皮肤包,以及有人特意保留下来的离群项。报错信息会指出相差最远的两个文件夹,并告诉你该运行什么命令(见一个皮肤包,一种文件夹形状)。它默认关闭,是为 folderskin-community 的工作流准备的。
应用如何读取皮肤包
Section titled “应用如何读取皮肤包”社区有列表和画廊两种视图,点任意皮肤包上的查看就能打开它:在添加之前,每款皮肤都会以文件夹的样子连同名称展示出来。刷新会重新读取列表。如果你添加过的皮肤包后来有了变化,会显示更新,点它会把其中的皮肤换成新版本。文件夹会保留现有的图标,新旧两个版本共有的图片如果被你收藏过,也会继续留在收藏里。
- folderskin-community 中的
index.json列出了每个皮肤包:ID、名称、作者、许可协议、标签、皮肤数量,以及其完整内容(pack.json和每张图片)的哈希。应用会把哈希和添加的皮肤一起保存,以此判断皮肤包有没有更新。每一项还记录了皮肤包的首次发布时间("added",单位为 Unix 秒,也就是用最初的 ID 添加它的pack.json的那次提交的时间),对于official.json里列出的皮肤包,还有"official": true。与皮肤包列表并列的"moved"就是 moved.json。index.json由folderskin-tools packs index生成,同时生成的还有previews/<id>.png,也就是把皮肤包的前四款皮肤画成文件夹排成一排的预览图。两者都在 folderskin-community 的main分支上自动生成,切勿手动编辑。 v2/由packs catalog生成,内容还是这些皮肤包,只是组织成了应用可以在你电脑上搜索的目录。其中的head.json指明当前的目录,列出featured和official皮肤包,带有moved,还列出提供同一目录树的镜像,比如https://packs.folderskin.app(见镜像)。应用会先从镜像获取每个文件,镜像失败时再从 GitHub 获取,无论来源如何,都会用哈希校验每个文件。从 0.1.7 开始,head.json本身也会优先从https://packs.folderskin.app读取。- 只有在你添加皮肤包时,应用才会下载其中的图片,每次同时下载四张,并显示已经下载了多少张。它会按上述限制检查每张图片,只要有一张不通过就什么也不保存。全部通过后再一起保存,所以皮肤包永远不会只添加一半。
FOLDERSKIN_COMMUNITY_URL可以让应用改用另一份 folderskin-community。例如,在本地仓库的根目录用python3 -m http.server启动服务,再把它设为http://localhost:8000,就能端到端地完整试用一个皮肤包。
index.json 和 head.json 会随着时间增加字段,每个版本的应用都只读取自己认识的字段,其余的直接跳过。pack.json 正好相反:它不接受格式规范之外的任何字段,所以永远不能往里加新东西。皮肤包的任何新信息都放在索引里。
精选与官方皮肤包
Section titled “精选与官方皮肤包”folderskin-community 的根目录下,与 packs/ 并列放着两份列表,只有维护者可以编辑。每份都是一个由皮肤包 ID 组成的 JSON 列表,比如 ["classic-art-k7q2mx", "colours-a2b3c4"]:
| 文件 | 作用 |
|---|---|
featured.json | 首次启动时推荐、在社区中排在最前面的皮肤包,按此顺序排列 |
official.json | 在社区、皮肤包查看页和网站上标为官方的皮肤包 |
两份列表都是可选的。每个 ID 都必须对应 packs/ 中的一个皮肤包,重复列出的只算一次。只要任一列表指向了不存在的皮肤包,packs index 和 packs catalog 就会停下来,什么也不写,所以改名或删除皮肤包不会留下空缺。pull request 的检查会运行 packs catalog,在合并之前就能发现问题。packs rename 会自动改写这两份列表。folderskin-community 的 Packs 工作流会在其 paths 中的文件变化时重建索引,所以这两份列表和 moved.json 都应该和 packs/** 一起写在 paths 里。
folderskin://install?pack=<id> 会打开 FolderSkin,在社区中定位到皮肤包 <id> 并添加它,和点它的添加按钮完全一样,进度显示和最后的提示也相同。窗口会先切换到最前面。如果重新向 GitHub 查询后,社区里仍然没有这个 ID 的皮肤包,FolderSkin 会告诉你,并建议你搜索一下。如果皮肤包已经在你的皮肤库里,就直接打开它,并提示已经添加过了。从 0.1.7 开始,带旧 ID 的链接会打开它迁移后的皮肤包(见 moved.json)。
只有完全符合下列格式的链接,FolderSkin 才会接受:scheme 是 folderskin,install 作为主机名(folderskin://install?…)或整个路径(folderskin:install?…),不带用户名、密码或端口,并且只有一个 pack 参数,它的值必须是皮肤包 ID(由小写字母和数字组成的单词,用单个连字符连接,最多 40 个字符)。其他参数会被跳过,其他任何形式的链接都会被忽略。
这个 scheme 由安装程序注册:macOS 应用的 Info.plist、Windows 安装程序,以及 Linux .deb 和 .rpm 的桌面条目。AppImage 没有安装步骤,所以会在启动时自行注册。在 Windows 和 Linux 上,点击链接会启动第二个 FolderSkin,它把链接交给已经在运行的那个后就退出,所以始终只有一个在运行。
试一试:
| macOS | 构建应用(pnpm tauri build --bundles app),然后打开一次 target/release/bundle/macos/FolderSkin.app,让 macOS 注册这个 scheme(最稳妥的做法是在 /Applications 里放一份),再运行 open 'folderskin://install?pack=classic-art'。macOS 只会把链接发给打包好的应用,所以 pnpm tauri dev 永远收不到链接 |
| Windows | 安装一个构建版本,或者运行 pnpm tauri dev(开发版本会为自己注册这个 scheme),然后在命令提示符中运行 start "" "folderskin://install?pack=classic-art",或者在“运行”对话框(Windows+R)中输入同样的链接 |
| Linux | 安装 .deb 或 .rpm,或者启动一次 AppImage,或者运行 pnpm tauri dev,然后运行 xdg-open 'folderskin://install?pack=classic-art' |
| 浏览器预览 | 运行 pnpm dev,然后打开 http://localhost:14200/?install=classic-art |
在 Windows 或 Linux 上运行 pnpm tauri dev 之前,请先退出已安装的 FolderSkin:如果已经有一个在运行,新启动的会把链接交给它,然后自己退出。开发版本注册的 scheme 会一直保留,直到安装程序或另一个构建版本重新注册。
安装次数统计
Section titled “安装次数统计”从社区添加一个皮肤包后,FolderSkin 会把这个皮肤包的 ID 告诉社区服务:POST https://community.folderskin.app/v1/packs/<id>/installs,不带请求体。它发送的仅此而已:没有账号,没有设备 ID,也没有关于你的皮肤库或文件夹的任何信息(和 FolderSkin 发出的所有请求一样,User-Agent 里会写明应用的版本)。这个请求在皮肤包保存之后才发出,五秒没有响应就放弃,不会让任何操作等待它,失败了也不会报错。开发版本,以及从另一份副本读取皮肤包的版本(FOLDERSKIN_COMMUNITY_URL),都不会发送任何内容,除非 FOLDERSKIN_COMMUNITY_API 指定了要发送到的服务。
对每个网络和每个皮肤包,服务每天只计一次添加,而且只统计已发布的 index.json 中的皮肤包。以旧 ID 添加的会计入它迁移后的皮肤包,所以 0.1.7 之前的应用也照样计数。服务为每个皮肤包保存一个计数,并在当天(UTC)剩下的时间里保存请求来源网络的加盐哈希,这样同一天重复添加同一个皮肤包不会被计两次。每天的清理任务会删除这些哈希。不保存任何地址。
folderskin.app 从 GET https://community.folderskin.app/v1/packs/installs 读取计数:{"version": 1, "installs": {"classic-art-k7q2mx": 42}},缓存五分钟。详见 services/community/README.md。