Author SHA1 Message Date
ZacharyZhang-NYandClaude Fable 5 f9c9b3c364 Bring the landing site into the repo as site/
Vite 8 vanilla page for omarchycn.zacharyzhang.com: kami palette on a
12-col Swiss grid, self-hosted Fusion Pixel zh_hans (OFL shipped),
GSAP ScrollSmoother/ScrollTrigger with reduced-motion opt-out, reicon
icons, ISO download plus one-click convert command copy, real
installer screenshot. shots.mjs is the rendered-state harness; deploy
is npx wrangler deploy from site/ (custom domain + workers.dev).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hLK3wsDuKVAC37GgDqg6H
2026-08-27 20:07:43 -04:00
ZacharyZhang-NYandClaude Fable 5 f72a48f747 One-click convert between vanilla Omarchy and OmarchyCN
omarchy-cn-convert turns a package-based install into OmarchyCN in one
curl-able run: registry key + [omarchycn] repo, --ask=4 swap onto the
cn-built packages (Chinese menu/bindings and OMARCHY CN branding ride
the settings tree), [omarchy] mirror line, zh_CN locale, fcitx5-rime
seeding, CN Arch mirrors, and a plymouth rebrand, recording pre-state
in /var/lib/omarchycn/convert-state. omarchy-cn-revert restores that
state symmetrically and survives user-removed packages, self-deletion
mid-swap, and cosmetic plymouth failures after the state is cleared.
Checkout installs are refused toward the overlay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hLK3wsDuKVAC37GgDqg6H
2026-08-27 18:20:51 -04:00
ZacharyZhang-NYandClaude Fable 5 c51df9977b Publish cn-built packages to the omarchycn registry before each release
packages/publish-cn-packages.sh uploads the omarchy-dev and
omarchy-settings-dev builds (token via curl config on stdin), and the
release checklist runs it as step 8 ahead of the public Release so the
one-click convert never installs stale packages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hLK3wsDuKVAC37GgDqg6H
2026-08-27 18:20:51 -04:00
ZacharyZhang-NYandClaude Fable 5 1535e7871f Bump cn/release to 7 after publishing 4.0.0.alpha-cn.6
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hLK3wsDuKVAC37GgDqg6H
2026-08-27 18:20:51 -04:00
23 changed files with 2327 additions and 8 deletions
+3 -1
View File
@@ -15,13 +15,15 @@ matching guide before starting:
This fork carries the OmarchyCN China-integration layer on top of upstream `basecamp/omarchy`: This fork carries the OmarchyCN China-integration layer on top of upstream `basecamp/omarchy`:
- `bin/omarchycn` routes to `omarchy cn <command>`; all cn commands are `bin/omarchy-cn-*` and follow the upstream bin conventions (metadata, helpers, `$OMARCHY_PATH`sole exception: the overlay installer bootstraps by resolving its own checkout) - `bin/omarchycn` routes to `omarchy cn <command>`; all cn commands are `bin/omarchy-cn-*` and follow the upstream bin conventions (metadata, helpers, `$OMARCHY_PATH`bootstrap exceptions: the overlay installer resolves its own checkout, and `omarchy-cn-convert` / `omarchy-cn-revert` are curl-able standalone scripts that default `OMARCHY_PATH` because they run before/while the cn tree exists)
- Convert/revert: `omarchy-cn-convert` turns a vanilla package-based Omarchy into OmarchyCN (registry key + [omarchycn] repo, `--ask=4` package swap to the cn-built omarchy-dev/omarchy-settings-dev published by `packages/publish-cn-packages.sh`, [omarchy] mirror line, zh_CN locale, fcitx5 seeding, plymouth rebrand) recording pre-state in `/var/lib/omarchycn/convert-state`; `omarchy-cn-revert` restores that state; checkout installs are refused toward the overlay
- `cn/` holds the data layer: `mirrors.json`, `dev-mirrors.json`, `apps.json`, `registry/` (AI providers/harnesses/compatibility), `fcitx5/`, `fontconfig/`, `keys/`, `lib/` (sourced helpers), `release` (cn release number) - `cn/` holds the data layer: `mirrors.json`, `dev-mirrors.json`, `apps.json`, `registry/` (AI providers/harnesses/compatibility), `fcitx5/`, `fontconfig/`, `keys/`, `lib/` (sourced helpers), `release` (cn release number)
- cn migrations live in `cn/migrations/*.sh`, run by `omarchy-cn-update` with per-file completion markers under `~/.local/state/omarchycn/` - cn migrations live in `cn/migrations/*.sh`, run by `omarchy-cn-update` with per-file completion markers under `~/.local/state/omarchycn/`
- Packaging: `packages/omarchy-pkgs-cn.patch` must be applied to the sibling `omarchy-pkgs` checkout so `omarchy-dev` ships `cn/`; keyring in `packages/omarchycn-keyring/` - Packaging: `packages/omarchy-pkgs-cn.patch` must be applied to the sibling `omarchy-pkgs` checkout so `omarchy-dev` ships `cn/`; keyring in `packages/omarchycn-keyring/`
- Upstream repo mirror: `packages/sync-omarchy-repo.sh` + `.gitea/workflows/pkg-repo-sync.yml` mirror the upstream `[omarchy]` stable channel into the Gitea Arch registry every 6h; `pacman-stable.conf` lists the mirror first (upstream fallback), clients must trust the registry key (install import + cn migration), `omarchycn doctor mirror --fix` re-heals the line; see `docs/pacman-repo.md` - Upstream repo mirror: `packages/sync-omarchy-repo.sh` + `.gitea/workflows/pkg-repo-sync.yml` mirror the upstream `[omarchy]` stable channel into the Gitea Arch registry every 6h; `pacman-stable.conf` lists the mirror first (upstream fallback), clients must trust the registry key (install import + cn migration), `omarchycn doctor mirror --fix` re-heals the line; see `docs/pacman-repo.md`
- ISO: `packages/omarchy-iso-cn.patch` must be applied to the sibling `omarchy-iso` checkout — Chinese installer (cage+foot graphical console with English VT fallback) and live-env packages - ISO: `packages/omarchy-iso-cn.patch` must be applied to the sibling `omarchy-iso` checkout — Chinese installer (cage+foot graphical console with English VT fallback) and live-env packages
- Release process: `docs/release-checklist.md`; signing: `docs/release-signing.md`; pacman repo: `docs/pacman-repo.md` - Release process: `docs/release-checklist.md`; signing: `docs/release-signing.md`; pacman repo: `docs/pacman-repo.md`
- Site: `site/` is the omarchycn.zacharyzhang.com landing page (Vite 8 vanilla + GSAP + self-hosted Fusion Pixel + reicon, kami palette on a 12-col grid); deploy with `npx wrangler deploy` from `site/`, rendered-state checks via `node shots.mjs` against a preview or the live URL
- Chinese-first defaults: OmarchyCN ships Simplified Chinese as the default UX. User-visible strings in `default/omarchy/omarchy-menu.jsonc` (all labels), `default/hypr/bindings/*.lua` (bind descriptions, including the generated workspace/group/panel loops), `bin/omarchy-menu-keybindings` (its merge list and priority patterns must match the shipped Chinese descriptions), `bin/omarchy-update-confirm`, and `install/user/first-run/*` notifications are Chinese with brand names kept in English; menu search stays reachable in English through leaf ids. When syncing upstream, translate new strings in these files and resolve string conflicts toward our Chinese text. - Chinese-first defaults: OmarchyCN ships Simplified Chinese as the default UX. User-visible strings in `default/omarchy/omarchy-menu.jsonc` (all labels), `default/hypr/bindings/*.lua` (bind descriptions, including the generated workspace/group/panel loops), `bin/omarchy-menu-keybindings` (its merge list and priority patterns must match the shipped Chinese descriptions), `bin/omarchy-update-confirm`, and `install/user/first-run/*` notifications are Chinese with brand names kept in English; menu search stays reachable in English through leaf ids. When syncing upstream, translate new strings in these files and resolve string conflicts toward our Chinese text.
- Default set changes vs upstream: `install/omarchy-base.packages` adds fcitx5-rime/chinese-addons/configtool and drops aether/libreoffice-fresh/obs-studio; the Basecamp/Discord/HEY/Google/WhatsApp/X launchers, their webapp keybindings, the whatsapp-slim extension, and the HEY mailto handler are removed (preinstall add/remove lists, launcher.hides, chromium flags, and mimeapps stay in sync); `install/user/cn-chinese.sh` seeds IME/font config on zh_CN systems - Default set changes vs upstream: `install/omarchy-base.packages` adds fcitx5-rime/chinese-addons/configtool and drops aether/libreoffice-fresh/obs-studio; the Basecamp/Discord/HEY/Google/WhatsApp/X launchers, their webapp keybindings, the whatsapp-slim extension, and the HEY mailto handler are removed (preinstall add/remove lists, launcher.hides, chromium flags, and mimeapps stay in sync); `install/user/cn-chinese.sh` seeds IME/font config on zh_CN systems
- AI: `cn/registry/` drives providers (DeepSeek/Kimi/Z.AI/MiniMax/Ollama-local) with zero-code adapters; Ollama uses a fixed `static_token` and runtime-listed models (`models_dynamic`); `omarchy-default-agent` also accepts kimi (official installer script, not mise), deepcode, dim, and dsh (mise npm); the Default Agent menu carries them plus an AI Hub combo entry - AI: `cn/registry/` drives providers (DeepSeek/Kimi/Z.AI/MiniMax/Ollama-local) with zero-code adapters; Ollama uses a fixed `static_token` and runtime-listed models (`models_dynamic`); `omarchy-default-agent` also accepts kimi (official installer script, not mise), deepcode, dim, and dsh (mise npm); the Default Agent menu carries them plus an AI Hub combo entry
+19 -2
View File
@@ -18,9 +18,10 @@
- **中国镜像管理**Arch / npm / pip / Cargo / Go 等软件源测速、自动选择与故障切换 - **中国镜像管理**Arch / npm / pip / Cargo / Go 等软件源测速、自动选择与故障切换
- **中文环境开箱即用**zh_CN locale、思源黑体 / 宋体字体栈、高分屏分数缩放预设 - **中文环境开箱即用**zh_CN locale、思源黑体 / 宋体字体栈、高分屏分数缩放预设
- **中文输入法**Fcitx5 + Rime 预配置,Wayland / GTK / Qt / Electron 全栈兼容,快捷键冲突自动处理 - **中文输入法**Fcitx5 + Rime 预配置,Wayland / GTK / Qt / Electron 全栈兼容,快捷键冲突自动处理
- **AI Hub**Kimi、DeepSeek、Z.AI/GLM 一等 Provider 支持,与 Claude Code、Codex、OpenCode、Kimi Code、Deep Code 等 Harness 的统一配置向导、凭据安全存储与连接诊断 - **AI Hub**Kimi、DeepSeek、Z.AI/GLM、MiniMax 与本地 Ollama 的一等 Provider 支持,与 Claude Code、Codex、OpenCode、Kimi Code、Deep Code 等 Harness 的统一配置向导、凭据安全存储与连接诊断;另有 Dim、DSH 等自管登录的 Agent CLI 一键安装
- **国内应用中心**:微信、QQ、飞书、钉钉、腾讯会议、WPS 等应用的可信安装入口 - **国内应用中心**:微信、QQ、飞书、钉钉、腾讯会议、WPS 等应用的可信安装入口
- **Overlay 安装器**:在现有 Omarchy 上叠加 OmarchyCN,全程可逆、可卸载 - **一键转换**:原版 Omarchy(包安装)一条命令转成 OmarchyCN,logo/菜单/键位/输入法/软件源全套切换,`omarchy cn revert` 一键还原
- **Overlay 安装器**:在现有 Omarchy checkout 上叠加 OmarchyCN,全程可逆、可卸载
- **统一诊断**`omarchycn doctor` 覆盖网络、镜像、输入法、显示与 AI 配置 - **统一诊断**`omarchycn doctor` 覆盖网络、镜像、输入法、显示与 AI 配置
## 下载与安装 ## 下载与安装
@@ -31,6 +32,22 @@
sha256sum -c SHA256SUMS.txt sha256sum -c SHA256SUMS.txt
``` ```
## 已有原版 Omarchy?一键转换
包安装的原版 Omarchy(quattro)可直接转换,无需重装系统:
```bash
curl -fsSL https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/raw/branch/quattro/bin/omarchy-cn-convert | bash
```
转换内容:换装 OmarchyCN 构建的系统包(中文菜单/键位/更新界面、OMARCHY CN 品牌、AI 全家桶)、启用 [omarchy] 自建镜像与 [omarchycn] 仓库、zh_CN 语言与 Fcitx5+Rime 输入法、国内 Arch 镜像自动测速。重新登录后生效。
随时一键还原(按转换时记录的状态逐项回退):
```bash
omarchy cn revert
```
## 上游文档 ## 上游文档
Omarchy 完整英文手册在 [`manual/`](manual/) 目录,亦见 [learn.omacom.io](https://learn.omacom.io/2/the-omarchy-manual)。 Omarchy 完整英文手册在 [`manual/`](manual/) 目录,亦见 [learn.omacom.io](https://learn.omacom.io/2/the-omarchy-manual)。
+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
# omarchy:summary=Convert a vanilla package-based Omarchy install into OmarchyCN
# omarchy:examples=omarchy cn convert
# Curl-able bootstrap (like the overlay installer): everything before the
# package swap must run without the cn tree, so no $OMARCHY_PATH until then.
set -euo pipefail
GITEA=${GITEA:-https://git.zacharyzhang.com}
OWNER=${OWNER:-ZacharyZhang-NY}
REGISTRY="$GITEA/api/packages/$OWNER/arch"
REG_KEY_FPR=74DCF57ACD812B24D959F146BD386048867B33B4
STATE_DIR=/var/lib/omarchycn
STATE=$STATE_DIR/convert-state
# Bootstrap exception: curl|bash runs outside a session, so default the path
export OMARCHY_PATH=${OMARCHY_PATH:-/usr/share/omarchy}
main() {
command -v pacman > /dev/null || { echo "需要 Arch Linux(未找到 pacman" >&2; exit 1; }
if (( EUID == 0 )); then
echo "请以普通用户运行(脚本内部按需 sudo" >&2
exit 1
fi
local prev_omarchy prev_settings
prev_omarchy=$(pacman -Q omarchy 2> /dev/null || pacman -Q omarchy-dev 2> /dev/null || true)
prev_settings=$(pacman -Q omarchy-settings 2> /dev/null || pacman -Q omarchy-settings-dev 2> /dev/null || true)
if [[ -z $prev_omarchy || -z $prev_settings ]]; then
if [[ -d $HOME/.local/share/omarchy/.git ]]; then
echo "检测到 checkout 安装(非包安装):请改用 overlaybin/omarchycn-install-overlay" >&2
else
echo "未检测到 Omarchy 包安装(需要 omarchy 与 omarchy-settings" >&2
fi
exit 1
fi
[[ -f $STATE ]] && echo "已转换过($STATE 在案):本次仅同步更新,不覆盖回退状态"
echo "==> 信任 OmarchyCN registry 公钥并配置 [omarchycn] 仓库"
local tmpkey
tmpkey=$(mktemp)
curl -fsSL "$REGISTRY/repository.key" -o "$tmpkey"
sudo pacman-key --add "$tmpkey"
sudo pacman-key --lsign-key "$REG_KEY_FPR"
rm -f "$tmpkey"
if ! grep -q '^\[omarchycn\]' /etc/pacman.conf; then
printf '\n[omarchycn]\nSigLevel = Required DatabaseOptional\nServer = %s/omarchycn/x86_64\n' \
"$REGISTRY" | sudo tee -a /etc/pacman.conf > /dev/null
fi
if [[ ! -f $STATE ]]; then
echo "==> 记录转换前状态(供 omarchy cn revert 还原)"
sudo mkdir -p "$STATE_DIR"
if [[ -f /etc/locale.conf ]]; then
sudo cp /etc/locale.conf "$STATE_DIR/locale.conf.pre"
fi
sudo cp /etc/pacman.d/mirrorlist "$STATE_DIR/mirrorlist.pre"
local p new_pkgs=""
for p in fcitx5-rime fcitx5-chinese-addons fcitx5-configtool omarchycn-keyring; do
pacman -Q "$p" > /dev/null 2>&1 || new_pkgs+="$p "
done
# Values are quoted because revert sources this file; publish atomically
{
echo "prev_omarchy=\"${prev_omarchy%% *}\""
echo "prev_settings=\"${prev_settings%% *}\""
echo "new_pkgs=\"${new_pkgs% }\""
echo "converted_at=\"$(date +%s)\""
} | sudo tee "$STATE.new" > /dev/null
sudo mv "$STATE.new" "$STATE"
fi
echo "==> 系统更新并换装 OmarchyCN 软件包(替换 ${prev_omarchy%% *}/${prev_settings%% *}"
sudo pacman -Syu --noconfirm
sudo pacman -S --noconfirm omarchycn/omarchycn-keyring
# --ask=4 auto-confirms removing the conflicting upstream omarchy packages
sudo pacman -S --noconfirm --ask=4 omarchycn/omarchy-dev omarchycn/omarchy-settings-dev
sudo pacman -S --needed --noconfirm fcitx5-rime fcitx5-chinese-addons fcitx5-configtool
echo "==> [omarchy] 仓库启用自建镜像(Cloudflare 上游兜底)"
source /usr/share/omarchy/cn/lib/mirror.sh
cn_mirror_omarchy_repo_fix
echo "==> 系统语言 zh_CN.UTF-8"
grep -q '^zh_CN.UTF-8 UTF-8' /etc/locale.gen ||
echo 'zh_CN.UTF-8 UTF-8' | sudo tee -a /etc/locale.gen > /dev/null
sudo locale-gen > /dev/null
if grep -q '^LANG=' /etc/locale.conf 2> /dev/null; then
sudo sed -i 's/^LANG=.*/LANG=zh_CN.UTF-8/' /etc/locale.conf
else
echo 'LANG=zh_CN.UTF-8' | sudo tee -a /etc/locale.conf > /dev/null
fi
echo "==> 中文输入法与字体(用户级)"
OMARCHY_PATH=/usr/share/omarchy bash /usr/share/omarchy/install/user/cn-chinese.sh
systemctl --user restart omarchy-fcitx5.service 2> /dev/null || true
echo "==> Arch 官方仓库切换国内镜像(自动测速,失败则保留现状)"
omarchy-cn-mirror-apply china || echo "国内镜像不可达,保留当前 mirrorlist"
echo "==> OMARCHY CN 开机画面"
omarchy-refresh-plymouth
omarchy-notification-send "OmarchyCN" "转换完成:重新登录后菜单/键位/输入法全中文" 2> /dev/null || true
echo
echo "转换完成(重新登录生效)。回退: omarchy cn revert"
}
main "$@"
+89
View File
@@ -0,0 +1,89 @@
#!/bin/bash
# omarchy:summary=Revert an OmarchyCN conversion back to vanilla Omarchy
# omarchy:examples=omarchy cn revert
# Curl-able and self-deleting-safe: main() is fully parsed before the package
# swap removes this file from the cn tree it may be running from.
set -euo pipefail
STATE_DIR=/var/lib/omarchycn
STATE=$STATE_DIR/convert-state
MIRROR_LINE="Server = https://git.zacharyzhang.com/api/packages/ZacharyZhang-NY/arch/omarchy/x86_64"
REG_KEY_FPR=74DCF57ACD812B24D959F146BD386048867B33B4
# Bootstrap exception: curl|bash runs outside a session, so default the path
export OMARCHY_PATH=${OMARCHY_PATH:-/usr/share/omarchy}
main() {
if [[ ! -f $STATE ]]; then
echo "未发现转换状态($STATE):本机不是由 omarchy cn convert 转换而来" >&2
exit 1
fi
if (( EUID == 0 )); then
echo "请以普通用户运行(脚本内部按需 sudo" >&2
exit 1
fi
local prev_omarchy="" prev_settings="" new_pkgs=""
# State is trusted root-owned key=value lines written by convert
source "$STATE"
[[ -n $prev_omarchy && -n $prev_settings ]] || { echo "转换状态损坏: $STATE" >&2; exit 1; }
echo "==> 清理转换时播种的用户配置(与 cn 层原件一致才删除)"
local seeded
for seeded in \
"$HOME/.config/fcitx5/profile:/usr/share/omarchy/cn/fcitx5/profile" \
"$HOME/.config/fontconfig/conf.d/64-omarchycn-cjk.conf:/usr/share/omarchy/cn/fontconfig/64-omarchycn-cjk.conf"; do
if cmp -s "${seeded%%:*}" "${seeded#*:}" 2> /dev/null; then
rm -f "${seeded%%:*}"
fi
done
echo "==> 还原 mirrorlist 与系统语言"
sudo cp "$STATE_DIR/mirrorlist.pre" /etc/pacman.d/mirrorlist
if [[ -f $STATE_DIR/locale.conf.pre ]]; then
sudo cp "$STATE_DIR/locale.conf.pre" /etc/locale.conf
fi
echo "==> pacman.conf 移除 [omarchycn] 与 [omarchy] 镜像行"
sudo sed -i "\|^$MIRROR_LINE\$|d" /etc/pacman.conf
# Delete the section header and body, but keep any section that follows
sudo sed -i '/^\[omarchycn\]$/,/^\[/{ /^\[omarchycn\]$/d; /^\[/!d; }' /etc/pacman.conf
echo "==> 换回上游软件包($prev_omarchy/$prev_settings"
sudo pacman -Sy
# --ask=4 auto-confirms removing the conflicting cn packages
sudo pacman -S --noconfirm --ask=4 "omarchy/$prev_omarchy" "omarchy/$prev_settings"
if [[ -n $new_pkgs ]]; then
# Only what is still installed: a user may have removed some already,
# and a -R failure here would strand the swap-completed system
local p still=""
for p in $new_pkgs; do
# grep without -q consumes the whole list: -q's early exit would
# SIGPIPE pacman and read as "not installed" under pipefail
pacman -Qq | grep -xF "$p" > /dev/null && still+="$p "
done
if [[ -n $still ]]; then
echo "==> 移除转换时新装的包: ${still% }"
# shellcheck disable=SC2086
sudo pacman -R --noconfirm ${still% }
fi
fi
echo "==> 移除 registry 公钥信任"
sudo pacman-key --delete "$REG_KEY_FPR" > /dev/null 2>&1 || true
# The system is vanilla again: clear state before the cosmetic tail so a
# plymouth failure cannot strand a reverted machine in "converted" state
systemctl --user restart omarchy-fcitx5.service 2> /dev/null || true
sudo rm -rf "$STATE_DIR"
echo "==> 还原上游开机画面"
if ! omarchy-refresh-plymouth; then
echo "开机画面还原失败:手动执行 omarchy-refresh-plymouth 即可" >&2
exit 1
fi
echo
echo "已回退为原版 Omarchy(重新登录生效)。再次转换: omarchy cn convert"
}
main "$@"
+1 -1
View File
@@ -1 +1 @@
6 7
+1
View File
@@ -418,5 +418,6 @@
"omarchycn.diagnostics.mirror-fix": {"icon":"󰇧","label":"镜像修复","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-doctor mirror --fix'"}, "omarchycn.diagnostics.mirror-fix": {"icon":"󰇧","label":"镜像修复","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-cn-doctor mirror --fix'"},
"omarchycn.diagnostics.ime": {"icon":"󰌌","label":"输入法状态","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ime-status"}, "omarchycn.diagnostics.ime": {"icon":"󰌌","label":"输入法状态","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-ime-status"},
"omarchycn.update": {"icon":"","label":"更新","when":"[[ -f ~/.local/state/omarchycn/overlay-manifest ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-update"}, "omarchycn.update": {"icon":"","label":"更新","when":"[[ -f ~/.local/state/omarchycn/overlay-manifest ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-update"},
"omarchycn.revert": {"icon":"󰕍","label":"回退为原版 Omarchy","when":"[[ -f /var/lib/omarchycn/convert-state ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-revert"},
"omarchycn.about": {"icon":"","label":"关于","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-status"}, "omarchycn.about": {"icon":"","label":"关于","action":"omarchy-launch-floating-terminal-with-presentation omarchy-cn-status"},
} }
+5 -3
View File
@@ -9,9 +9,11 @@
5. `sha256sum``SHA256SUMS.txt`;签名 SUMS 与 ISO(发布子钥) 5. `sha256sum``SHA256SUMS.txt`;签名 SUMS 与 ISO(发布子钥)
6. SBOM 两份:syftlive airootfs+ 离线仓库 .PKGINFO 采集 6. SBOM 两份:syftlive airootfs+ 离线仓库 .PKGINFO 采集
7. `release.json`(版本、双向提交、包版本表、迁移列表、min_compatible、产物清单)→ 签名 release.json 7. `release.json`(版本、双向提交、包版本表、迁移列表、min_compatible、产物清单)→ 签名 release.json
8. 建 tag 与 Release,上传全部产物,Release Notes 写明上游基线与已知问题 8. 把本次构建的 `omarchy-dev` / `omarchy-settings-dev` 发布到 [omarchycn] registry
9. 匿名回读已发布 ISO 并 sha256 复核 == 本地构建值 `packages/publish-cn-packages.sh`;先于公开 Release,一键转换才不会装到旧包)
10. `cn/release` 数字 +1,提交 9. 建 tag 与 Release,上传全部产物,Release Notes 写明上游基线与已知问题
10. 匿名回读已发布 ISO 并 sha256 复核 == 本地构建值
11. `cn/release` 数字 +1,提交
## 版本规则 ## 版本规则
+21 -1
View File
@@ -24,7 +24,27 @@ omarchycn setup
向导依次配置:语言、时区、显示缩放、中文 locale、中文字体、Fcitx5+Rime 输入法、输入法切换键、pacman 镜像、开发工具镜像、国内应用、AI Hub、隐私说明。每一步都可跳过,中断后重跑会从未完成的步骤继续。 向导依次配置:语言、时区、显示缩放、中文 locale、中文字体、Fcitx5+Rime 输入法、输入法切换键、pacman 镜像、开发工具镜像、国内应用、AI Hub、隐私说明。每一步都可跳过,中断后重跑会从未完成的步骤继续。
## 现有 Omarchy 叠加安装(Overlay ## 原版 Omarchy 一键转换(包安装
quattro 的原版 Omarchy 是包安装形态(`pacman -Q omarchy` 有输出),可以整机转换成 OmarchyCN,无需重装:
```bash
curl -fsSL https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/raw/branch/quattro/bin/omarchy-cn-convert | bash
```
转换做的事:信任 OmarchyCN registry 公钥并接入 [omarchycn] 仓库;把 `omarchy`/`omarchy-settings` 换成 OmarchyCN 构建的 `omarchy-dev`/`omarchy-settings-dev`(中文菜单、中文键位描述、OMARCHY CN 品牌、AI 全家桶随包生效);给 `[omarchy]` 仓库加自建镜像(Cloudflare 上游兜底);系统语言切到 zh_CN.UTF-8 并安装配置 Fcitx5+Rime;Arch 官方仓库自动测速切换国内镜像;刷新 OMARCHY CN 开机画面。重新登录后生效。
转换前的包名、语言与 mirrorlist 会记录在 `/var/lib/omarchycn/convert-state`,随时一键还原:
```bash
omarchy cn revert
```
还原会换回上游软件包、恢复语言与 mirrorlist、移除 [omarchycn] 仓库与镜像行、卸载转换时新装的输入法包,并恢复上游开机画面。菜单 OmarchyCN 区段也有「回退为原版 Omarchy」入口。
## 现有 Omarchy 叠加安装(Overlaycheckout 形态)
老的 checkout 安装(`~/.local/share/omarchy` 是 git 检出)用 Overlay 叠加,不走一键转换:
```bash ```bash
git clone https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn.git git clone https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn.git
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# Publishes cn-built packages into the Gitea [omarchycn] Arch registry.
# usage: publish-cn-packages.sh <pkg.tar.zst>... (maintainer/release step)
set -euo pipefail
GITEA=${GITEA:-https://git.zacharyzhang.com}
OWNER=${OWNER:-ZacharyZhang-NY}
: "${PKG_SYNC_TOKEN:?PKG_SYNC_TOKEN (Gitea package-write token) is required}"
(($# > 0)) || { echo "usage: publish-cn-packages.sh <pkg.tar.zst>..." >&2; exit 1; }
for pkg in "$@"; do
[[ -f $pkg ]] || { echo "no such file: $pkg" >&2; exit 1; }
# Token travels via curl config on stdin, never in the process argument list
code=$(curl -sS --retry 2 -o /tmp/publish.out -w '%{http_code}' -X PUT -K - \
--upload-file "$pkg" "$GITEA/api/packages/$OWNER/arch/omarchycn" \
<<< "header = \"Authorization: token $PKG_SYNC_TOKEN\"")
case "$code" in
201) echo "published: ${pkg##*/}" ;;
409) echo "already present: ${pkg##*/}" ;;
*)
echo "upload of ${pkg##*/} failed (HTTP $code): $(head -c 300 /tmp/publish.out)" >&2
exit 1
;;
esac
done
+27
View File
@@ -0,0 +1,27 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
shots/
TASKS.md
.wrangler/
+117
View File
@@ -0,0 +1,117 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OmarchyCN · 为中国重铸的 Omarchy</title>
<meta name="description" content="OmarchyCN:华丽、现代、观点鲜明的 Arch + Hyprland 桌面,中国版。下载 ISO 全新安装,或一条命令把原版 Omarchy 转换为 OmarchyCN。" />
<link rel="icon" href="/favicon.svg" />
<link rel="preload" href="/fonts/fusion-pixel-12px-proportional-zh_hans.otf.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/fusion-pixel-12px-monospaced-zh_hans.otf.woff2" as="font" type="font/woff2" crossorigin />
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<div id="smooth-wrapper">
<div id="smooth-content">
<div class="wrap">
<header class="band topbar">
<p class="brand" style="grid-column: 1 / 7">OMARCHY CN</p>
<nav class="topnav" style="grid-column: 7 / 13" aria-label="站点">
<a href="https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/src/branch/quattro/manual/zh-cn/01-install.md">手册</a>
<a href="https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases">发布</a>
<a href="https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn">源码</a>
</nav>
</header>
<main>
<section class="band hero" aria-labelledby="hero-title">
<p class="eyebrow" style="grid-column: 1 / 13">OMARCHY CN · 4.0.0.alpha-cn.6</p>
<h1 id="hero-title" style="grid-column: 1 / 12">
<span class="line">华丽、现代、观点鲜明</span><span class="line">的 Linux。中国版。</span>
</h1>
<p class="lede" style="grid-column: 1 / 9">
基于 Arch + Hyprland 的 Omarchy 下游发行版:中文体验、国内软件源与 AI 工具链,开箱即用。
</p>
<div class="actions" style="grid-column: 1 / 13">
<a
class="btn"
href="https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases/download/4.0.0.alpha-cn.6/omarchy-2026.08.27-x86_64-local.iso"
>
<span class="i" data-icon="Download" aria-hidden="true"></span>下载 ISO<span class="btn-meta">5.9 GB</span>
</a>
<div class="convert">
<p class="cmd-label" id="cmd-label">已有原版 Omarchy?一键转换:</p>
<div class="cmd" aria-labelledby="cmd-label">
<code id="cmd-text">curl -fsSL https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/raw/branch/quattro/bin/omarchy-cn-convert | bash</code>
<button class="copy" id="copy-btn" type="button" aria-label="复制转换命令">
<span class="i" data-icon="Copy" aria-hidden="true"></span><span class="copy-text">复制</span>
</button>
</div>
</div>
</div>
<p class="meta mono" style="grid-column: 1 / 13">
上游基线 basecamp/omarchy quattro · sha256 4c1cc4a2…60bcca · 转换可随时 omarchy cn revert 还原
</p>
</section>
<section class="band shotband" aria-label="安装器截图">
<figure class="shot" style="grid-column: 1 / 13">
<img src="/shot.png" width="1280" height="800" alt="OmarchyCN 安装器欢迎屏:像素风 OMARCHY CN 字标,按回车开始安装" loading="lazy" />
<figcaption class="mono">UEFI 安装器 · 全程中文 · 无人值守可选</figcaption>
</figure>
</section>
<section class="band feats" aria-label="特性">
<article class="feat" style="grid-column: 1 / 4">
<p class="num mono">01</p>
<h2>开箱中文</h2>
<p>安装器、菜单、键位说明、Fcitx5 + Rime 输入法与 CJK 字体,全程 zh_CN。</p>
</article>
<article class="feat" style="grid-column: 4 / 7">
<p class="num mono">02</p>
<h2>国内软件源</h2>
<p>上游 [omarchy] 仓库自建镜像每 6 小时同步,Cloudflare 原源兜底。</p>
</article>
<article class="feat" style="grid-column: 7 / 10">
<p class="num mono">03</p>
<h2>AI 全家桶</h2>
<p>DeepSeek、Kimi、GLM、MiniMax 与本地 OllamaClaude Code 到 DSH 一键就位。</p>
</article>
<article class="feat" style="grid-column: 10 / 13">
<p class="num mono">04</p>
<h2>随时回退</h2>
<p>转换前状态全程记录,omarchy cn revert 一条命令还原为原版。</p>
</article>
</section>
<section class="band verify" aria-labelledby="verify-title">
<h2 id="verify-title" style="grid-column: 1 / 4">校验</h2>
<dl class="mono" style="grid-column: 4 / 13">
<div><dt>sha256</dt><dd>4c1cc4a2726a28c6f2e548234be13381556bc2e9da9b7426d02c821bbd60bcca</dd></div>
<div><dt>签名子钥</dt><dd>PGP 211B9B82E17CFB67</dd></div>
<div>
<dt>清单</dt>
<dd><a href="https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases/download/4.0.0.alpha-cn.6/SHA256SUMS.txt">SHA256SUMS.txt</a> · <a href="https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases/download/4.0.0.alpha-cn.6/SHA256SUMS.txt.asc">SHA256SUMS.txt.asc</a></dd>
</div>
</dl>
</section>
</main>
<footer class="band foot">
<p style="grid-column: 1 / 8">
OmarchyCN 是独立社区发行版,与 Basecamp、37signals 及 Omarchy 官方无隶属关系。代码沿用上游 MIT 许可证。
</p>
</footer>
<div class="guides" aria-hidden="true">
<div class="cols"></div>
<div class="rows"></div>
</div>
</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+1185
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
{
"name": "omarchycn-site",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"puppeteer-core": "^25.9.0",
"vite": "^8.2.2"
},
"dependencies": {
"gsap": "^3.15.0",
"reicon": "^1.2.1"
}
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="6" fill="#1B365D"/><path fill-rule="evenodd" d="M8 8h16v16H8Zm4 4v8h8v-8Z" fill="#f5f4ed"/></svg>

After

Width:  |  Height:  |  Size: 191 B

+96
View File
@@ -0,0 +1,96 @@
Fusion Pixel Font
https://github.com/TakWolf/fusion-pixel-font
Copyright (c) 2022, TakWolf (https://takwolf.com).
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+70
View File
@@ -0,0 +1,70 @@
// Rendered-state verification: full-page shots at key widths + interaction states
import puppeteer from "puppeteer-core";
import { mkdirSync } from "node:fs";
const CHROME = process.env.CHROME;
const BASE = process.env.BASE ?? "http://localhost:4173";
if (!CHROME) throw new Error("CHROME env (chrome.exe path) is required");
mkdirSync("shots", { recursive: true });
const browser = await puppeteer.launch({ executablePath: CHROME, headless: "new" });
const page = await browser.newPage();
// Layout passes: reduced motion = static truth of the full page
await page.emulateMediaFeatures([{ name: "prefers-reduced-motion", value: "reduce" }]);
for (const w of [1440, 1280, 960, 375]) {
await page.setViewport({ width: w, height: 900 });
await page.goto(BASE, { waitUntil: "networkidle0" });
await page.evaluate(() => document.fonts.ready);
await new Promise((r) => setTimeout(r, 1400));
await page.screenshot({ path: `shots/w${w}.png`, fullPage: true });
console.log(`shot w${w}`);
}
// Motion pass: real scroll, reveals must land every section at opacity 1
await page.emulateMediaFeatures([{ name: "prefers-reduced-motion", value: "no-preference" }]);
await page.setViewport({ width: 1440, height: 900 });
await page.goto(BASE, { waitUntil: "networkidle0" });
await new Promise((r) => setTimeout(r, 1200));
await page.evaluate(async () => {
for (let y = 0; y <= document.body.scrollHeight; y += 300) {
window.scrollTo(0, y);
await new Promise((r) => setTimeout(r, 60));
}
});
await new Promise((r) => setTimeout(r, 1200));
const opacities = await page.evaluate(() =>
[".shot", ".feat", ".verify h2", ".foot p"].map(
(s) => `${s}=${getComputedStyle(document.querySelector(s)).opacity}`,
),
);
console.log("post-scroll opacities:", opacities.join(" "));
await page.screenshot({ path: "shots/motion-bottom.png" });
await page.setViewport({ width: 1440, height: 900 });
await page.goto(BASE, { waitUntil: "networkidle0" });
await new Promise((r) => setTimeout(r, 1400));
await page.keyboard.press("g");
await new Promise((r) => setTimeout(r, 300));
await page.screenshot({ path: "shots/grid-overlay.png", fullPage: false });
console.log("shot grid-overlay");
await page.keyboard.press("g");
const ctx = browser.defaultBrowserContext();
await ctx.overridePermissions(BASE, ["clipboard-read", "clipboard-write", "clipboard-sanitized-write"]).catch(() => {});
await page.hover(".cmd");
await new Promise((r) => setTimeout(r, 200));
await page.screenshot({ path: "shots/cmd-hover.png" });
await page.click("#copy-btn");
await new Promise((r) => setTimeout(r, 400));
const state = await page.evaluate(() => ({
label: document.querySelector(".copy-text").textContent,
clip: navigator.clipboard.readText ? undefined : "n/a",
}));
const clip = await page.evaluate(() => navigator.clipboard.readText().catch(() => "READ-FAIL"));
console.log("copy label:", state.label, "| clipboard:", clip.slice(0, 60) + "…");
await page.screenshot({ path: "shots/cmd-copied.png" });
await browser.close();
console.log("SHOTS-DONE");
+95
View File
@@ -0,0 +1,95 @@
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { ScrollSmoother } from "gsap/ScrollSmoother";
import { Download, Copy, Check } from "reicon";
const ICONS = { Download, Copy, Check };
const ICON_OPTS = { size: 18, strokeWidth: 1.5 };
for (const el of document.querySelectorAll(".i[data-icon]")) {
const icon = ICONS[el.dataset.icon];
if (!icon) throw new Error(`unknown icon: ${el.dataset.icon}`);
el.replaceChildren(icon(ICON_OPTS));
}
/* Copy the convert command */
const copyBtn = document.getElementById("copy-btn");
const copyText = copyBtn.querySelector(".copy-text");
const copyIcon = copyBtn.querySelector(".i");
copyText.setAttribute("aria-live", "polite");
let copyTimer;
let copySeq = 0;
copyBtn.addEventListener("click", async () => {
const cmd = document.getElementById("cmd-text").textContent.trim();
const seq = ++copySeq;
let ok = true;
try {
await navigator.clipboard.writeText(cmd);
} catch (err) {
console.error("clipboard write failed", err);
ok = false;
}
// Only the latest click may touch the feedback state
if (seq !== copySeq) return;
clearTimeout(copyTimer);
copyBtn.classList.remove("done", "fail");
copyBtn.classList.add(ok ? "done" : "fail");
copyIcon.replaceChildren(ok ? Check(ICON_OPTS) : Copy(ICON_OPTS));
copyText.textContent = ok ? "已复制" : "复制失败";
copyTimer = setTimeout(() => {
copyBtn.classList.remove("done", "fail");
copyIcon.replaceChildren(Copy(ICON_OPTS));
copyText.textContent = "复制";
}, 2000);
});
/* Grid overlay: G key */
document.addEventListener("keydown", (e) => {
if (e.key !== "g" && e.key !== "G") return;
if (e.metaKey || e.ctrlKey || e.altKey) return;
if (/^(input|textarea|select)$/i.test(e.target.tagName)) return;
document.body.classList.toggle("grid-on");
});
/* Motion: smooth scrolling + reveals, skipped under reduced motion */
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
ScrollSmoother.create({
wrapper: "#smooth-wrapper",
content: "#smooth-content",
smooth: 1,
});
const enter = [".hero .eyebrow", ".hero h1 .line", ".hero .lede", ".hero .actions", ".hero .meta"];
gsap.set(enter, { y: 24, autoAlpha: 0 });
document.fonts.ready.then(() => {
gsap.to(enter, {
y: 0,
autoAlpha: 1,
duration: 0.7,
ease: "power2.out",
stagger: 0.09,
});
});
// Document-tail elements never reach the 85% line, so they fire on entry
const reveals = [
[".shot", "top 85%"],
[".feat", "top 85%"],
[".verify > *", "top 92%"],
[".foot > *", "top bottom"],
];
for (const [sel, start] of reveals) {
for (const el of gsap.utils.toArray(sel)) {
gsap.from(el, {
y: 24,
autoAlpha: 0,
duration: 0.6,
ease: "power2.out",
scrollTrigger: { trigger: el, start },
});
}
}
}
+419
View File
@@ -0,0 +1,419 @@
/* OmarchyCN landing — kami palette on a Müller-Brockmann grid, Fusion Pixel type */
@font-face {
font-family: "Fusion Pixel";
src: url("/fonts/fusion-pixel-12px-proportional-zh_hans.otf.woff2") format("woff2");
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "Fusion Pixel Mono";
src: url("/fonts/fusion-pixel-12px-monospaced-zh_hans.otf.woff2") format("woff2");
font-weight: 400;
font-display: swap;
}
:root {
--cols: 12;
--bl: 8px;
--lh: 24px;
--gutter: 24px;
--margin: 72px;
--pad: 24px;
--maxw: 1296px;
--paper: #f5f4ed;
--ivory: #faf9f5;
--sand: #e8e6dc;
--ink: #141413;
--ink-2: #3d3d3a;
--stone: #6b6a64;
--brand: #1b365d;
--brand-light: #2d5a8a;
--tint: #eef2f7;
--line: #d8d5c8;
--shot-bg: #141318;
--err: #8a2d2d;
--pixel: "Fusion Pixel", "Noto Sans SC", system-ui, sans-serif;
--pixel-mono: "Fusion Pixel Mono", ui-monospace, monospace;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
background: var(--paper);
color: var(--ink);
font-family: var(--pixel);
font-size: 12px;
line-height: var(--lh);
-webkit-font-smoothing: none;
text-rendering: geometricPrecision;
}
.mono {
font-family: var(--pixel-mono);
}
a {
color: var(--ink);
text-decoration-thickness: 1px;
text-underline-offset: 4px;
transition: color 150ms ease-out;
}
a:hover {
color: var(--brand);
}
:is(a, button):focus-visible {
outline: 2px solid var(--brand);
outline-offset: 2px;
}
/* Grid frame */
.wrap {
position: relative;
max-width: var(--maxw);
margin: 0 auto;
padding: var(--pad) var(--margin) 96px;
}
.band {
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
column-gap: var(--gutter);
row-gap: var(--lh);
align-items: start;
}
/* Topbar */
.topbar {
align-items: center;
}
.brand {
font-size: 12px;
letter-spacing: 2px;
}
.topnav {
display: flex;
justify-content: flex-end;
gap: 24px;
}
.topnav a {
color: var(--stone);
text-decoration: none;
}
.topnav a:hover {
color: var(--brand);
text-decoration: underline;
}
/* Hero */
.hero {
margin-top: 144px;
}
.eyebrow {
font-family: var(--pixel-mono);
color: var(--brand);
letter-spacing: 1px;
}
h1 {
font-size: 60px;
line-height: 72px;
font-weight: 400;
word-break: keep-all;
}
h1 .line {
display: block;
}
.lede {
font-size: 24px;
line-height: 40px;
color: var(--ink-2);
margin-top: 8px;
}
.actions {
margin-top: 24px;
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
column-gap: var(--gutter);
row-gap: var(--lh);
align-items: end;
}
.btn {
grid-column: 1 / 4;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 48px;
padding: 0 24px;
background: var(--brand);
color: var(--ivory);
border-radius: 8px;
text-decoration: none;
transition: background-color 150ms ease-out;
}
.btn:hover {
background: var(--brand-light);
color: var(--ivory);
}
.btn .i {
color: var(--ivory);
}
.btn-meta {
color: var(--sand);
}
.convert {
grid-column: 4 / 11;
}
.cmd-label {
color: var(--stone);
margin-bottom: 8px;
}
.cmd {
position: relative;
display: flex;
align-items: flex-start;
gap: 16px;
background: var(--ivory);
border: 1px solid var(--line);
border-radius: 8px;
padding: 12px 16px;
}
.cmd code {
font-family: var(--pixel-mono);
font-size: 12px;
line-height: 24px;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
}
.copy {
position: relative;
display: flex;
align-items: center;
gap: 4px;
border: 0;
background: none;
font: inherit;
font-family: var(--pixel-mono);
color: var(--stone);
cursor: pointer;
padding: 0 4px;
height: 24px;
border-radius: 4px;
transition: color 150ms ease-out, background-color 150ms ease-out;
}
.copy::after {
content: "";
position: absolute;
inset: -8px;
}
.cmd:hover .copy,
.copy:hover,
.copy:focus-visible {
color: var(--brand);
}
.copy:hover {
background: var(--tint);
}
.copy.done {
color: var(--brand);
}
.copy.fail {
color: var(--err);
}
.meta {
color: var(--stone);
margin-top: 8px;
}
/* Screenshot */
.shotband {
margin-top: 96px;
}
.shot {
background: var(--shot-bg);
border: 1px solid var(--line);
border-radius: 8px;
padding: 24px;
}
.shot img {
display: block;
width: 100%;
height: auto;
outline: 1px solid rgba(255, 255, 255, 0.1);
outline-offset: -1px;
}
.shot figcaption {
color: var(--sand);
margin-top: 16px;
}
/* Features */
.feats {
margin-top: 144px;
}
.num {
color: var(--brand);
}
.feat h2 {
font-size: 24px;
line-height: 32px;
font-weight: 400;
margin: 16px 0 8px;
}
.feat p:last-child {
color: var(--stone);
}
/* Verify */
.verify {
margin-top: 144px;
padding-top: 24px;
border-top: 1px solid var(--line);
}
.verify h2 {
font-size: 24px;
line-height: 32px;
font-weight: 400;
}
.verify dl > div {
display: grid;
grid-template-columns: 96px 1fr;
gap: 24px;
margin-bottom: 8px;
}
.verify dt {
color: var(--stone);
}
.verify dd {
word-break: break-all;
}
/* Footer */
.foot {
margin-top: 144px;
padding-top: 24px;
border-top: 1px solid var(--line);
color: var(--stone);
}
.foot a {
color: var(--stone);
}
/* Icons */
.i {
display: inline-flex;
width: 24px;
height: 24px;
align-items: center;
justify-content: center;
color: currentColor;
}
/* Grid overlay (toggle: G or footer button) */
.guides {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
}
body.grid-on .guides {
opacity: 1;
}
.guides .cols {
position: absolute;
top: 0;
bottom: 0;
left: var(--margin);
right: var(--margin);
display: grid;
grid-template-columns: repeat(var(--cols), 1fr);
column-gap: var(--gutter);
}
.guides .cols::before {
content: "";
grid-column: 1 / -1;
background: repeating-linear-gradient(
to right,
rgba(27, 54, 93, 0.1) 0,
rgba(27, 54, 93, 0.1) calc((100% - 11 * var(--gutter)) / 12),
transparent 0,
transparent calc((100% - 11 * var(--gutter)) / 12 + var(--gutter))
);
}
.guides .rows {
position: absolute;
left: var(--margin);
right: var(--margin);
top: var(--pad);
bottom: 0;
background-image: repeating-linear-gradient(
to bottom,
rgba(27, 54, 93, 0.22) 0 1px,
transparent 1px var(--lh)
);
}
/* Responsive */
@media (max-width: 960px) {
:root {
--margin: 24px;
}
h1 {
font-size: 36px;
line-height: 48px;
}
h1 .line {
display: inline;
}
.lede {
font-size: 12px;
line-height: 24px;
}
.hero {
margin-top: 96px;
}
.band > * {
grid-column: 1 / -1 !important;
}
.feat {
grid-column: span 6 !important;
}
.btn {
grid-column: 1 / 7;
}
.convert {
grid-column: 1 / -1;
}
.topbar .brand {
grid-column: 1 / 5 !important;
}
.topbar .topnav {
grid-column: 5 / 13 !important;
}
}
@media (max-width: 640px) {
.feat {
grid-column: 1 / -1 !important;
}
.btn {
grid-column: 1 / -1;
}
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"name": "omarchycn-site",
"compatibility_date": "2026-08-27",
"assets": {
"directory": "./dist"
},
"workers_dev": true,
"routes": [
{
"pattern": "omarchycn.zacharyzhang.com",
"custom_domain": true
}
]
}
+12
View File
@@ -109,6 +109,18 @@ printf 'Server = https://pkgs.omarchy.org/stable/$arch\n' > "$tmpconf"
rm -f "$tmpconf" rm -f "$tmpconf"
pass "omarchy mirror guard ignores comments and matches only the exact line" pass "omarchy mirror guard ignores comments and matches only the exact line"
# Convert/revert bootstrap pair: parse, self-delete guard, no constant drift
for s in omarchy-cn-convert omarchy-cn-revert; do
bash -n "$ROOT/bin/$s" || fail "$s parses"
grep -q '^main "\$@"$' "$ROOT/bin/$s" || fail "$s wraps work in main() against self-deletion"
done
grep -q -- '--ask=4' "$ROOT/bin/omarchy-cn-convert" || fail "convert auto-confirms the conflict swap"
lib_mirror=$(grep -m1 '^CN_OMARCHY_MIRROR=' "$ROOT/cn/lib/mirror.sh" | cut -d'"' -f2)
revert_mirror=$(grep -m1 '^MIRROR_LINE=' "$ROOT/bin/omarchy-cn-revert" | cut -d'"' -f2)
[[ "Server = $lib_mirror" == "$revert_mirror" ]] ||
fail "revert MIRROR_LINE drifted from cn/lib/mirror.sh" "$revert_mirror"
pass "convert/revert pair: parse, main() guard, ask=4, mirror constant in sync"
# Menu: OmarchyCN entries parse and reference only existing commands # Menu: OmarchyCN entries parse and reference only existing commands
node -e ' node -e '
const fs = require("fs"); const fs = require("fs");