Compare commits
@@ -0,0 +1,24 @@
|
||||
name: pkg-repo-sync
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "17 */6 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: quattro
|
||||
|
||||
- name: Mirror upstream [omarchy] stable into the Gitea Arch registry
|
||||
env:
|
||||
PKG_SYNC_TOKEN: ${{ secrets.PKG_SYNC_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if ! command -v zstd > /dev/null; then
|
||||
apt-get update -qq && apt-get install -y -qq zstd
|
||||
fi
|
||||
bash packages/sync-omarchy-repo.sh
|
||||
@@ -11,13 +11,32 @@ matching guide before starting:
|
||||
- [`agents/skills/visual-verification.md`](agents/skills/visual-verification.md) - verifying any change with a visual effect in the running UI
|
||||
- [`agents/skills/migrations.md`](agents/skills/migrations.md) - creating or changing migrations under `migrations/`
|
||||
|
||||
# OmarchyCN Layer
|
||||
|
||||
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` — 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 migrations live in `cn/migrations/*.sh`, run by `omarchy-cn-migrate` with per-file completion markers under `~/.local/state/omarchycn/`; `bin/omarchy-migrate` calls it after the upstream migrations, and `omarchy-cn-update` serves both install forms (overlay: git pull + reinstall + migrate; package: exec `omarchy-update`)
|
||||
- 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`
|
||||
- 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`; ISO distribution: Cloudflare R2 bucket behind `dl.zacharyzhang.com` via `packages/upload-release-r2.sh` (Gitea releases carry only the small artifacts)
|
||||
- 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.
|
||||
- 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
|
||||
- Upstream sync: `.gitea/workflows/upstream-sync.yml` opens a PR per upstream change; keep upstream file edits minimal and inventoried (currently: one `GROUP_DESCRIPTIONS[cn]` line in `bin/omarchy`, the `omarchy-cn-migrate` line in `bin/omarchy-migrate`, the localized menu/bindings/update/first-run surfaces above, the western-app removals above, the cn agent roster in `bin/omarchy-default-agent` / `bin/omarchy-agent` (kimi/deepcode/dim/dsh), the `[omarchy]` mirror line in `default/pacman/pacman-stable.conf`, the registry-key import in `install/post-install/pacman.sh`, a rewritten `README.md` (known recurring sync conflict, resolve toward ours), `AGENTS.md` additions, and two `.gitignore` lines)
|
||||
- cn tests: `test/shell.d/omarchycn-test.sh`, `test/shell.d/omarchycn-ai-test.sh`; localized UI expectations live in the upstream suites (menu, keybindings-menu, hyprland-default-config, binding-conflicts, clock, screenrecording, update-disk-space)
|
||||
|
||||
# Documentation Layout
|
||||
|
||||
Three documentation trees, split by genre and audience:
|
||||
|
||||
- `agents/skills/` - task procedure ("do this when doing X"), for anyone working on the codebase
|
||||
- `docs/` - reference on how the system is shaped (file layout, update pipeline, theming, shell architecture), for anyone working on the codebase; skills link here for depth
|
||||
- `manual/` - end-user documentation for using Omarchy, published; never codebase internals
|
||||
- `manual/` - end-user documentation for using Omarchy, published; never codebase internals. Chinese user manual for the cn layer lives in `manual/zh-cn/`
|
||||
|
||||
# Style
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
面向中国开发者的 [Omarchy](https://omarchy.org) 下游发行版:完整保留 Omarchy 的 Arch + Hyprland 桌面体验,为中国网络环境、中文使用习惯与国内 AI 服务做系统级增强。
|
||||
|
||||
当前版本 `4.0.0.alpha-cn.1`(基于上游 `quattro` 分支构建的基线 ISO)。
|
||||
当前版本 `4.0.0.alpha-cn.7`(基于上游 `quattro` 分支构建的基线 ISO)。
|
||||
|
||||
## 特性
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
- Btrfs + Snapper 更新前快照与系统回滚
|
||||
- archiso 离线安装镜像,支持无人值守安装与双系统引导
|
||||
|
||||
OmarchyCN 增强(开发中,按路线图逐步交付):
|
||||
|
||||
- **中国镜像管理**:Arch / npm / pip / Cargo / Go 等软件源测速、自动选择与故障切换
|
||||
- **中文环境开箱即用**:zh_CN locale、思源黑体 / 宋体字体栈、高分屏分数缩放预设
|
||||
- **中文输入法**: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 等应用的可信安装入口
|
||||
- **Overlay 安装器**:在现有 Omarchy 上叠加 OmarchyCN,全程可逆、可卸载
|
||||
- **一键转换**:原版 Omarchy(包安装)一条命令转成 OmarchyCN,logo/菜单/键位/输入法/软件源全套切换,`omarchy cn revert` 一键还原
|
||||
- **Overlay 安装器**:在现有 Omarchy checkout 上叠加 OmarchyCN,全程可逆、可卸载
|
||||
- **统一诊断**:`omarchycn doctor` 覆盖网络、镜像、输入法、显示与 AI 配置
|
||||
|
||||
## 下载与安装
|
||||
@@ -33,6 +32,22 @@ OmarchyCN 增强(开发中,按路线图逐步交付):
|
||||
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)。
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Basecamp
|
||||
Exec=omarchy-launch-webapp https://launchpad.37signals.com
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=basecamp
|
||||
StartupNotify=true
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Discord
|
||||
Exec=omarchy-launch-webapp https://discord.com/channels/@me
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=omarchy-discord
|
||||
StartupNotify=true
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Google Contacts
|
||||
Exec=omarchy-launch-webapp https://contacts.google.com/
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=google-contacts
|
||||
StartupNotify=true
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Google Maps
|
||||
Exec=omarchy-launch-webapp https://maps.google.com
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=google-maps
|
||||
StartupNotify=true
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Google Messages
|
||||
Exec=omarchy-launch-webapp https://messages.google.com/web/conversations
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=google-messages
|
||||
StartupNotify=true
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Google Photos
|
||||
Exec=omarchy-launch-webapp https://photos.google.com/
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=google-photos
|
||||
StartupNotify=true
|
||||
@@ -1,9 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=HEY
|
||||
Exec=omarchy-webapp-handler-hey %u
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=hey
|
||||
StartupNotify=true
|
||||
MimeType=x-scheme-handler/mailto
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=WhatsApp
|
||||
Exec=omarchy-launch-webapp https://web.whatsapp.com/
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=whatsapp
|
||||
StartupNotify=true
|
||||
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=X
|
||||
Exec=omarchy-launch-webapp https://x.com/
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=x
|
||||
StartupNotify=true
|
||||
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 22 KiB |
@@ -41,6 +41,7 @@ GROUP_DESCRIPTIONS[clipboard]="Clipboard helpers"
|
||||
GROUP_DESCRIPTIONS[cmd]="Command and shortcut helpers"
|
||||
GROUP_DESCRIPTIONS[cn]="OmarchyCN China environment integration"
|
||||
GROUP_DESCRIPTIONS[config]="System configuration helpers"
|
||||
GROUP_DESCRIPTIONS[crash]="Crash notification controls"
|
||||
GROUP_DESCRIPTIONS[debug]="Diagnostics and support logs"
|
||||
GROUP_DESCRIPTIONS[finalize]="Finalize user setup"
|
||||
GROUP_DESCRIPTIONS[default]="Default application selection"
|
||||
|
||||
@@ -92,13 +92,39 @@ omp)
|
||||
;;
|
||||
ori)
|
||||
# Ori is a harness launcher, and `ori code` is the agent it runs itself.
|
||||
# A prompt alone means one headless turn there, printed after the turn ends,
|
||||
# so --interactive is what seeds the session with it and keeps the window.
|
||||
command=(ori code)
|
||||
[[ -n ${prompt:-} ]] && command+=(--prompt "$prompt")
|
||||
[[ -n ${prompt:-} ]] && command+=(--interactive --prompt "$prompt")
|
||||
;;
|
||||
pi)
|
||||
command=(pi)
|
||||
[[ -n ${prompt:-} ]] && command+=("$prompt")
|
||||
;;
|
||||
kimi)
|
||||
command=(kimi)
|
||||
[[ -n ${prompt:-} ]] && command+=(--prompt "$prompt")
|
||||
;;
|
||||
deepcode)
|
||||
command=(deepcode)
|
||||
[[ -n ${prompt:-} ]] && command+=("$prompt")
|
||||
;;
|
||||
dim)
|
||||
# `dim exec` is the documented one-shot prompt form
|
||||
if [[ -n ${prompt:-} ]]; then
|
||||
command=(dim exec "$prompt")
|
||||
else
|
||||
command=(dim)
|
||||
fi
|
||||
;;
|
||||
dsh)
|
||||
# dsh has no TUI: `dsh web` is the interactive form, headless the one-shot
|
||||
if [[ -n ${prompt:-} ]]; then
|
||||
command=(dsh --profile headless "$prompt")
|
||||
else
|
||||
command=(dsh web)
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported default agent: $agent" >&2
|
||||
exit 1
|
||||
|
||||
@@ -528,7 +528,7 @@ def fetch_codex_rpc():
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
[codex, "-s", "read-only", "-a", "untrusted", "app-server"],
|
||||
[codex, "-s", "read-only", "-a", "on-request", "app-server"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
# omarchy:args=[--no-osd] [+N%|N%-|N%]
|
||||
# omarchy:examples=omarchy brightness display apple | omarchy brightness display apple +5% | omarchy brightness display apple --no-osd 50%
|
||||
|
||||
device_cache="${XDG_RUNTIME_DIR:-/tmp}/omarchy-brightness-display-apple.device"
|
||||
# Only cache under the user-private runtime dir. With no XDG_RUNTIME_DIR we skip
|
||||
# caching (detect every run) rather than fall back to a predictable, world-writable
|
||||
# /tmp path another user could pre-create.
|
||||
device_cache=""
|
||||
if [[ -n ${XDG_RUNTIME_DIR:-} ]]; then
|
||||
device_cache="$XDG_RUNTIME_DIR/omarchy-brightness-display-apple.device"
|
||||
fi
|
||||
no_osd=0
|
||||
if [[ ${1:-} == "--no-osd" ]]; then
|
||||
no_osd=1
|
||||
@@ -28,9 +34,14 @@ find_apple_display_device() {
|
||||
local cached=""
|
||||
local device=""
|
||||
|
||||
if [[ -r $device_cache ]]; then
|
||||
if [[ -n $device_cache && -r $device_cache ]]; then
|
||||
read -r cached <"$device_cache" || true
|
||||
if [[ -n $cached && -e $cached ]]; then
|
||||
# Trust a cached value only if it still names a hiddev character device. A
|
||||
# stale or unexpected cache (a regular file, a non-hiddev node) is ignored and
|
||||
# we re-detect instead of handing an arbitrary path to asdcontrol. The globs
|
||||
# are left unquoted on purpose: [[ ]] pattern-matches an unquoted right side,
|
||||
# and quoting them would turn the match into a literal string comparison.
|
||||
if [[ ( $cached == /dev/hiddev* || $cached == /dev/usb/hiddev* ) && -c $cached ]]; then
|
||||
printf '%s\n' "$cached"
|
||||
return 0
|
||||
fi
|
||||
@@ -39,7 +50,9 @@ find_apple_display_device() {
|
||||
device="$(detect_apple_display_device)" || return 1
|
||||
[[ -n $device ]] || return 1
|
||||
|
||||
printf '%s\n' "$device" >"$device_cache"
|
||||
if [[ -n $device_cache ]]; then
|
||||
printf '%s\n' "$device" >"$device_cache"
|
||||
fi
|
||||
printf '%s\n' "$device"
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ if omarchy-cmd-missing "$command"; then
|
||||
$install
|
||||
fi
|
||||
|
||||
key=$(omarchy-cn-ai-secret get "$provider")
|
||||
key=$(cn_ai_resolve_key "$provider")
|
||||
|
||||
case "$harness" in
|
||||
claude-code)
|
||||
@@ -45,7 +45,8 @@ claude-code)
|
||||
settings="$HOME/.claude/settings.json"
|
||||
mkdir -p "${settings%/*}"
|
||||
base=$(cn_ai_endpoint "$provider" anthropic)
|
||||
fast=$(cn_ai_fast_model "$provider")
|
||||
# Dynamic-model providers have no fast alias: every tier maps to the model
|
||||
fast=$(cn_ai_fast_model "$provider" 2>/dev/null || printf '%s' "$model")
|
||||
current="{}"
|
||||
if [[ -s $settings ]]; then
|
||||
current=$(cat "$settings")
|
||||
|
||||
@@ -38,6 +38,8 @@ fi
|
||||
|
||||
if [[ $proto == "native" ]]; then
|
||||
echo "INFO ai: $harness 自管鉴权,跳过 OmarchyCN Key 检查"
|
||||
elif cn_ai_static_token "$provider" > /dev/null 2>&1; then
|
||||
echo "PASS ai: $provider 使用内置 token,无需存储 Key"
|
||||
elif omarchy-cn-ai-secret get "$provider" > /dev/null 2>&1; then
|
||||
echo "PASS ai: $provider API Key stored"
|
||||
else
|
||||
@@ -47,7 +49,7 @@ fi
|
||||
|
||||
if [[ $proto != "native" ]]; then
|
||||
base=$(cn_ai_endpoint "$provider" "$proto")
|
||||
host=$(sed -E 's|https?://([^/]+).*|\1|' <<<"$base")
|
||||
host=$(sed -E 's|https?://([^/:]+).*|\1|' <<<"$base")
|
||||
if getent hosts "$host" > /dev/null 2>&1; then
|
||||
echo "PASS ai: endpoint DNS resolves ($host)"
|
||||
else
|
||||
|
||||
@@ -32,7 +32,7 @@ fi
|
||||
|
||||
case "$config_method" in
|
||||
env)
|
||||
key=$(omarchy-cn-ai-secret get "$provider")
|
||||
key=$(cn_ai_resolve_key "$provider")
|
||||
if [[ $harness == "codex" ]]; then
|
||||
cn_ai_render_codex_config "$provider" "$model" "$key"
|
||||
exec "$command"
|
||||
|
||||
@@ -33,7 +33,9 @@ if [[ $level == "unsupported" ]]; then
|
||||
fi
|
||||
|
||||
allowlist=$(cn_ai_combo_models "$harness" "$provider")
|
||||
if [[ -n $allowlist ]]; then
|
||||
if cn_ai_models_dynamic "$provider"; then
|
||||
: # runtime-listed models; omarchycn ai test validates against the live server
|
||||
elif [[ -n $allowlist ]]; then
|
||||
# Combo-restricted model set (e.g. codex uses its own catalog slugs)
|
||||
if ! grep -qxF "$model" <<<"$allowlist"; then
|
||||
echo "Model $model not supported for $harness x $provider" >&2
|
||||
@@ -68,6 +70,6 @@ secret_ref = "omarchycn://ai/$provider"
|
||||
EOF
|
||||
|
||||
echo "Profile $name: $harness x $provider x $model (compat: $level)"
|
||||
if ! omarchy-cn-ai-secret get "$provider" > /dev/null 2>&1; then
|
||||
if ! cn_ai_resolve_key "$provider" > /dev/null 2>&1; then
|
||||
echo "提示: 尚未存储 $provider 的 API Key,运行: omarchycn ai secret set $provider"
|
||||
fi
|
||||
|
||||
@@ -24,7 +24,31 @@ provider=$(printf '%s\n' "${providers[@]}" | gum choose --header "选择 Provide
|
||||
|
||||
harness_proto=$(cn_ai_harness_field "$harness" protocol)
|
||||
allowlist=$(cn_ai_combo_models "$harness" "$provider")
|
||||
if [[ -n $allowlist ]]; then
|
||||
if cn_ai_models_dynamic "$provider"; then
|
||||
base=$(cn_ai_endpoint "$provider" "$harness_proto")
|
||||
if [[ $provider == "ollama" ]]; then
|
||||
if omarchy-cmd-missing ollama; then
|
||||
gum confirm "Ollama 未安装,现在安装本地服务?" || exit 1
|
||||
if omarchy-cmd-present nvidia-smi; then
|
||||
ollama_pkg=ollama-cuda
|
||||
elif omarchy-cmd-present rocminfo; then
|
||||
ollama_pkg=ollama-rocm
|
||||
else
|
||||
ollama_pkg=ollama
|
||||
fi
|
||||
omarchy-pkg-add "$ollama_pkg"
|
||||
fi
|
||||
if ! curl -sf --connect-timeout 3 "$base/api/tags" > /dev/null; then
|
||||
sudo systemctl enable --now ollama
|
||||
fi
|
||||
fi
|
||||
models=$(curl -sf --connect-timeout 3 "$base/api/tags" | jq -r '.models[].name' || true)
|
||||
if [[ -z $models ]]; then
|
||||
echo "无法列出本地模型(服务未运行或尚未拉取):先 ollama pull <模型>(如 qwen3-coder)再重试" >&2
|
||||
exit 1
|
||||
fi
|
||||
model=$(gum choose --header "选择本地模型" <<<"$models")
|
||||
elif [[ -n $allowlist ]]; then
|
||||
model=$(gum choose --header "选择模型" <<<"$allowlist")
|
||||
else
|
||||
# Hide models bound to a different wire protocol than the harness speaks
|
||||
@@ -40,12 +64,15 @@ proto=$(cn_ai_harness_field "$harness" protocol)
|
||||
if [[ $proto == "native" ]]; then
|
||||
omarchy-cn-ai-profile-create "$name" "$harness" "$provider" "$model"
|
||||
omarchy-cn-ai-profile-use "$name"
|
||||
echo "$harness 自管鉴权:首次启动时按其官方流程登录/配置"
|
||||
echo "$harness 自管鉴权:首次启动时在 CLI 内完成官方登录"
|
||||
if gum confirm "立即启动并完成官方登录?"; then
|
||||
exec omarchy-cn-ai-launch "$name"
|
||||
fi
|
||||
echo "完成。启动: omarchycn ai launch"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! omarchy-cn-ai-secret get "$provider" > /dev/null 2>&1; then
|
||||
if ! cn_ai_resolve_key "$provider" > /dev/null 2>&1; then
|
||||
key=$(gum input --password --header "$provider API Key(获取: $(jq -r --arg p "$provider" '.providers[$p].key_url' "$CN_AI_PROVIDERS"))")
|
||||
printf '%s' "$key" | omarchy-cn-ai-secret set "$provider"
|
||||
fi
|
||||
@@ -73,4 +100,10 @@ fi
|
||||
|
||||
rm -f "$profile_file.omarchycn-prev"
|
||||
omarchy-cn-ai-profile-use "$name"
|
||||
|
||||
if [[ $harness == "claude-code" || $harness == "codex" ]]; then
|
||||
if gum confirm "设为系统默认 Agent(Super+Shift+Ctrl+A)?"; then
|
||||
omarchy-cn-ai-default "$name"
|
||||
fi
|
||||
fi
|
||||
echo "完成。启动: omarchycn ai launch"
|
||||
|
||||
@@ -31,7 +31,7 @@ fi
|
||||
base="${OMARCHYCN_AI_TEST_BASE_URL:-$(cn_ai_endpoint "$provider" "${proto%-responses}")}"
|
||||
echo "INFO ai-test: $profile ($harness x $provider x $model) -> $base"
|
||||
|
||||
if ! key=$(omarchy-cn-ai-secret get "$provider" 2>/dev/null); then
|
||||
if ! key=$(cn_ai_resolve_key "$provider" 2>/dev/null); then
|
||||
echo "FAIL ai-test: 未存储 $provider 的 API Key (omarchycn ai secret set $provider)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -32,6 +32,17 @@ fi
|
||||
|
||||
case "$source_type" in
|
||||
aur)
|
||||
# Offline installs leave only offline.db in sync; without the official
|
||||
# repo dbs yay treats repo deps as AUR. Upgrades must go through the
|
||||
# omarchy-update pipeline (pacman guard).
|
||||
if [[ ! -f /var/lib/pacman/sync/core.db ]]; then
|
||||
if [[ ! -t 0 ]]; then
|
||||
echo "首次安装应用前请先完成一次系统更新: omarchy-update" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "首次安装应用前需要完成一次系统更新。"
|
||||
omarchy-update
|
||||
fi
|
||||
omarchy-pkg-aur-add "$(jq -r '.package' <<<"$entry")"
|
||||
;;
|
||||
webapp)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Show or set the OmarchyCN release channel
|
||||
# omarchy:args=[stable|beta|nightly]
|
||||
# omarchy:examples=omarchycn channel | omarchycn channel beta
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CHANNEL_FILE="$HOME/.config/omarchycn/channel"
|
||||
|
||||
if (( $# == 0 )); then
|
||||
if [[ -f $CHANNEL_FILE ]]; then
|
||||
cat "$CHANNEL_FILE"
|
||||
else
|
||||
echo "beta (default)"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
stable | beta | nightly)
|
||||
mkdir -p "${CHANNEL_FILE%/*}"
|
||||
echo "$1" > "$CHANNEL_FILE"
|
||||
echo "Channel: $1"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown channel: $1 (stable|beta|nightly)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -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 安装(非包安装):请改用 overlay(bin/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 "$@"
|
||||
@@ -69,6 +69,29 @@ check_mirror() {
|
||||
fi
|
||||
}
|
||||
|
||||
# refresh-pacman restores the checkout's template, which on overlay installs
|
||||
# has no OmarchyCN mirror line; this check makes that loss visible and fixable
|
||||
check_omarchy_repo() {
|
||||
local conf="${OMARCHYCN_PACMAN_CONF:-/etc/pacman.conf}"
|
||||
|
||||
if ! grep -q "^Server = https://pkgs.omarchy.org/stable/" "$conf"; then
|
||||
echo "INFO omarchy-repo: 非 stable 通道,跳过 OmarchyCN 镜像检查"
|
||||
return 0
|
||||
fi
|
||||
# --fix runs the helper even when the line is present: it also heals a missing key
|
||||
if [[ $fix == "true" ]]; then
|
||||
if cn_mirror_omarchy_repo_fix && cn_mirror_omarchy_repo_ok; then
|
||||
echo "PASS omarchy-repo: OmarchyCN 镜像行与 registry 公钥在位"
|
||||
return 0
|
||||
fi
|
||||
elif cn_mirror_omarchy_repo_ok; then
|
||||
echo "PASS omarchy-repo: OmarchyCN 镜像行在位"
|
||||
return 0
|
||||
fi
|
||||
echo "FAIL omarchy-repo: [omarchy] 缺 OmarchyCN 镜像行(omarchycn doctor mirror --fix 可修复)"
|
||||
failures=$((failures + 1))
|
||||
}
|
||||
|
||||
check_dev_mirror() {
|
||||
omarchy-cn-dev-mirror-doctor || failures=$((failures + 1))
|
||||
}
|
||||
@@ -79,12 +102,16 @@ check_ime() {
|
||||
|
||||
case "$module" in
|
||||
network) check_network ;;
|
||||
mirror) check_mirror ;;
|
||||
mirror)
|
||||
check_mirror
|
||||
check_omarchy_repo
|
||||
;;
|
||||
dev-mirror) check_dev_mirror ;;
|
||||
ime) check_ime ;;
|
||||
all)
|
||||
check_network
|
||||
check_mirror
|
||||
check_omarchy_repo
|
||||
check_dev_mirror
|
||||
check_ime
|
||||
;;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Install Kimi Code with the official installer script
|
||||
# omarchy:examples=omarchycn kimi install
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if omarchy-cmd-present kimi; then
|
||||
echo "Kimi Code 已安装"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
installer=$(mktemp)
|
||||
trap 'rm -f "$installer"' EXIT
|
||||
curl -fsSL https://code.kimi.com/kimi-code/install.sh -o "$installer"
|
||||
bash "$installer"
|
||||
|
||||
# The official installer only touches .bashrc PATH; expose kimi now
|
||||
if [[ ! -x $HOME/.kimi-code/bin/kimi ]]; then
|
||||
echo "kimi binary missing after the official installer" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
ln -sf "$HOME/.kimi-code/bin/kimi" "$HOME/.local/bin/kimi"
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Run pending OmarchyCN migrations
|
||||
# omarchy:examples=omarchycn migrate
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MIGRATIONS_DIR="$OMARCHY_PATH/cn/migrations"
|
||||
DONE_DIR="$HOME/.local/state/omarchycn/migrations-done"
|
||||
|
||||
[[ -d $MIGRATIONS_DIR ]] || exit 0
|
||||
|
||||
mkdir -p "$DONE_DIR"
|
||||
for m in "$MIGRATIONS_DIR"/*.sh; do
|
||||
[[ -f $m ]] || continue
|
||||
name="${m##*/}"
|
||||
if [[ ! -f $DONE_DIR/$name ]]; then
|
||||
echo "OmarchyCN migration: $name"
|
||||
bash -euo pipefail "$m"
|
||||
touch "$DONE_DIR/$name"
|
||||
fi
|
||||
done
|
||||
@@ -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,15 +1,17 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Update an overlay install: pull source, reinstall, run migrations
|
||||
# omarchy:summary=Full OmarchyCN update: system, cn data, and migrations
|
||||
# omarchy:examples=omarchycn update
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MANIFEST="$HOME/.local/state/omarchycn/overlay-manifest"
|
||||
MIGRATION_DONE_DIR="$HOME/.local/state/omarchycn/migrations-done"
|
||||
|
||||
if [[ ! -f $MANIFEST ]]; then
|
||||
echo "Not an overlay install (no $MANIFEST)." >&2
|
||||
echo "Package-based installs update through 'omarchy update' / pacman." >&2
|
||||
# Package install: the pipeline brings the new cn tree and runs cn migrations
|
||||
if [[ -d $OMARCHY_PATH/cn ]]; then
|
||||
exec omarchy-update
|
||||
fi
|
||||
echo "未检测到 OmarchyCN($OMARCHY_PATH/cn 不存在,也没有 overlay manifest)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -19,25 +21,52 @@ if [[ ! -d $src/.git ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
channel=$(omarchy-cn-channel)
|
||||
channel=${channel%% *}
|
||||
|
||||
old=$(git -C "$src" rev-parse --short HEAD)
|
||||
git -C "$src" pull --ff-only
|
||||
git -C "$src" fetch -q --tags origin
|
||||
|
||||
case "$channel" in
|
||||
nightly)
|
||||
git -C "$src" checkout -q quattro
|
||||
git -C "$src" pull --ff-only origin quattro
|
||||
;;
|
||||
beta)
|
||||
tag=$(git -C "$src" tag -l "*-cn.*" | sort -V | tail -1)
|
||||
if [[ -z $tag ]]; then
|
||||
echo "beta 通道无可用发布 tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
git -C "$src" checkout -q "$tag"
|
||||
;;
|
||||
stable)
|
||||
tag=$(git -C "$src" tag -l "*-cn.*" | grep -vE "alpha|beta|rc" | sort -V | tail -1 || true)
|
||||
if [[ -z $tag ]]; then
|
||||
echo "当前尚无 stable 发布(omarchycn channel beta 可跟随预发布)" >&2
|
||||
exit 1
|
||||
fi
|
||||
git -C "$src" checkout -q "$tag"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown channel: $channel" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Never move onto a tree that predates the channel or migrate mechanism:
|
||||
# it cannot switch back or finish this update's migration step
|
||||
if [[ ! -f $src/bin/omarchy-cn-channel || ! -f $src/bin/omarchy-cn-migrate ]]; then
|
||||
git -C "$src" checkout -q "$old"
|
||||
echo "$channel 通道的目标发布早于当前更新机制,已回退;请使用 nightly 或更新的发布" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
new=$(git -C "$src" rev-parse --short HEAD)
|
||||
echo "Source: $old -> $new"
|
||||
echo "Channel: $channel, source: $old -> $new"
|
||||
|
||||
"$src/bin/omarchy-cn-install-overlay"
|
||||
|
||||
# Run each not-yet-completed migration in filename order, one marker per file
|
||||
if [[ -d $src/cn/migrations ]]; then
|
||||
mkdir -p "$MIGRATION_DONE_DIR"
|
||||
for m in "$src"/cn/migrations/*.sh; do
|
||||
[[ -f $m ]] || continue
|
||||
name="${m##*/}"
|
||||
if [[ ! -f $MIGRATION_DONE_DIR/$name ]]; then
|
||||
echo "Migration: $name"
|
||||
bash -euo pipefail "$m"
|
||||
touch "$MIGRATION_DONE_DIR/$name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
"$src/bin/omarchy-cn-migrate"
|
||||
|
||||
echo "OmarchyCN update complete ($(omarchy-cn-version 2>/dev/null || echo unknown))"
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Silence crash notifications for one program, or list what is silenced
|
||||
# omarchy:args=[--] [<program>] [on|off|toggle]
|
||||
# omarchy:examples=omarchy crash mute | omarchy crash mute hyprland | omarchy crash mute /usr/bin/hyprland | omarchy crash mute hyprland off
|
||||
|
||||
# The flag omarchy-crash-watch reads before announcing a crash. Muting is per
|
||||
# program; Trigger > Toggle > Crash Capture is the switch for all of them.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
readonly MUTES="$HOME/.local/state/omarchy/toggles/crash-ignore"
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy crash mute [--] [<program>] [on|off|toggle]" >&2
|
||||
}
|
||||
|
||||
# Only regular files, because that is all the watcher honours: anything else in
|
||||
# there would be reported as muted while the crashes kept arriving. The dotted
|
||||
# glob is for a program legitimately called .hidden, and `.` and `..` fail the
|
||||
# same -f test that keeps them out.
|
||||
list() {
|
||||
local entry found=0
|
||||
|
||||
for entry in "$MUTES"/* "$MUTES"/.*; do
|
||||
[[ -f $entry ]] || continue
|
||||
printf '%s\n' "${entry##*/}"
|
||||
found=1
|
||||
done
|
||||
|
||||
((found)) || echo "No programs muted. Crashes all notify."
|
||||
}
|
||||
|
||||
# A program may be named -h, and the router answers that with its own help
|
||||
# before this ever runs. `omarchy crash mute -- -h` is the way through.
|
||||
[[ ${1:-} == "--" ]] && shift
|
||||
|
||||
if (($# == 0)); then
|
||||
list
|
||||
exit 0
|
||||
fi
|
||||
|
||||
program=$1
|
||||
action=${2:-on}
|
||||
|
||||
# The watcher keys the mute on the executable's basename, so accept the path it
|
||||
# reports as readily as the name, and reduce either the same way it does.
|
||||
program=${program##*/}
|
||||
|
||||
if [[ -z $program || $program == "." || $program == ".." ]]; then
|
||||
echo "Not a program name: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$action" in
|
||||
on|off|toggle) ;;
|
||||
*)
|
||||
echo "Not an action: $action" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
omarchy-toggle "crash-ignore/$program" "$action" || exit 1
|
||||
|
||||
# Report what is now true rather than what was asked for: the flag is what the
|
||||
# watcher reads, and a toggle does not say which way it went.
|
||||
if omarchy-toggle-enabled "crash-ignore/$program"; then
|
||||
echo "Muted crash notifications for $program."
|
||||
else
|
||||
echo "Crash notifications for $program are back on."
|
||||
fi
|
||||
@@ -48,12 +48,17 @@ announce() {
|
||||
# -n 0 so a restart does not re-announce crashes already dealt with.
|
||||
journalctl -f -n 0 -o json "MESSAGE_ID=$COREDUMP_MESSAGE_ID" 2>/dev/null |
|
||||
while IFS= read -r entry; do
|
||||
# A dash for a field that is empty as well as one that is missing: tab is
|
||||
# IFS whitespace, so an empty field collapses into the next delimiter and
|
||||
# every field after it shifts along one. A process can set its own comm to
|
||||
# nothing, and that crash used to be read as somebody else's and dropped.
|
||||
IFS=$'\t' read -r uid comm pid exe signal < <(
|
||||
jq -r '[(._UID // "-"),
|
||||
(.COREDUMP_COMM // "-"),
|
||||
(.COREDUMP_PID // "-"),
|
||||
(.COREDUMP_EXE // "-"),
|
||||
(.COREDUMP_SIGNAL_NAME // "-")] | @tsv' <<<"$entry" 2>/dev/null
|
||||
jq -r 'def field: if . == null or . == "" then "-" else . end;
|
||||
[(._UID | field),
|
||||
(.COREDUMP_COMM | field),
|
||||
(.COREDUMP_PID | field),
|
||||
(.COREDUMP_EXE | field),
|
||||
(.COREDUMP_SIGNAL_NAME | field)] | @tsv' <<<"$entry" 2>/dev/null
|
||||
)
|
||||
|
||||
[[ $pid =~ ^[0-9]+$ ]] || continue
|
||||
@@ -71,11 +76,29 @@ journalctl -f -n 0 -o json "MESSAGE_ID=$COREDUMP_MESSAGE_ID" 2>/dev/null |
|
||||
name=$comm
|
||||
[[ $exe == /* ]] && name=${exe##*/}
|
||||
|
||||
# A process can set its own comm to anything prctl takes, slashes included,
|
||||
# and a crash with no recorded executable falls back to it. The mute below
|
||||
# turns this name into a path, so keep it one component: a crash must not
|
||||
# reach a flag outside crash-ignore/, nor have a diagnosis write one there.
|
||||
name=${name##*/}
|
||||
|
||||
# What that leaves is not always a name. "/" leaves nothing, which is no
|
||||
# kind of array subscript and no kind of toast; a dot component names a
|
||||
# directory rather than a flag, so a mute on it would touch that directory
|
||||
# and then never match; and a dash is what the read above puts there when
|
||||
# the crash recorded no name at all.
|
||||
[[ -n $name && $name != "-" && $name != "." && $name != ".." ]] || name=unknown
|
||||
|
||||
[[ -n $ignore_pattern && $name =~ $ignore_pattern ]] && continue
|
||||
|
||||
# Never announce our own machinery, or it notifies about itself.
|
||||
[[ $name == omarchy-crash-* || $name == omarchy-agent-* ]] && continue
|
||||
|
||||
# Muted at the end of a diagnosis, when the user was offered it and said
|
||||
# yes. A flag per program rather than one list, so omarchy-crash-mute can
|
||||
# lift one without reading, rewriting and re-parsing the rest.
|
||||
omarchy-toggle-enabled "crash-ignore/$name" && continue
|
||||
|
||||
now=$EPOCHSECONDS
|
||||
(((now - ${last_notified[$name]:-0}) < dedupe_seconds)) && continue
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Set and launch the default coding agent
|
||||
# omarchy:args=[pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush]
|
||||
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude
|
||||
# omarchy:args=[pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush|kimi|deepcode|dim|dsh]
|
||||
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent kimi
|
||||
|
||||
installing=false
|
||||
if [[ ${1:-} == "--install" ]]; then
|
||||
@@ -34,25 +34,42 @@ crush) agent="crush"; name="Crush" ;;
|
||||
grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;;
|
||||
agy | antigravity | antigravity-cli | gemini | gemini-cli) agent="agy"; name="Antigravity"; agent_package="antigravity-cli" ;;
|
||||
copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;;
|
||||
kimi | kimi-code) agent="kimi"; name="Kimi Code" ;;
|
||||
deepcode | deep-code) agent="deepcode"; name="Deep Code"; agent_package="npm:@vegamo/deepcode-cli" ;;
|
||||
dim | dimcode | dim-agent) agent="dim"; name="Dim Agent"; agent_package="npm:dimcode" ;;
|
||||
dsh | deepseek-harness) agent="dsh"; name="DSH"; agent_package="npm:@deepseek-ai/dsh" ;;
|
||||
*)
|
||||
echo "Usage: omarchy-default-agent <pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush>"
|
||||
echo "Usage: omarchy-default-agent <pi|omp|opencode|ori|claude|codex|grok|agy|copilot|crush|kimi|deepcode|dim|dsh>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
agent_package=${agent_package:-$agent}
|
||||
|
||||
if [[ $installing == "false" ]] && ! mise where "$agent_package" &>/dev/null; then
|
||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent"
|
||||
fi
|
||||
# Kimi Code installs via its official script, not mise
|
||||
if [[ $agent == "kimi" ]]; then
|
||||
if omarchy-cmd-missing kimi; then
|
||||
if [[ $installing == "false" ]]; then
|
||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install kimi
|
||||
fi
|
||||
if ! omarchy-cn-kimi-install; then
|
||||
echo "Could not install $name with the official installer" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [[ $installing == "false" ]] && ! mise where "$agent_package" &>/dev/null; then
|
||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent"
|
||||
fi
|
||||
|
||||
if ! mise use -g "$agent_package"; then
|
||||
if [[ $installing == "true" ]]; then
|
||||
echo "Could not install $name with mise" >&2
|
||||
else
|
||||
echo "Could not set $name as the default coding agent" >&2
|
||||
if ! mise use -g "$agent_package"; then
|
||||
if [[ $installing == "true" ]]; then
|
||||
echo "Could not install $name with mise" >&2
|
||||
else
|
||||
echo "Could not set $name as the default coding agent" >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$agent_file")"
|
||||
|
||||
@@ -6,6 +6,18 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Whenever this runs as root — invoked directly through the passwordless
|
||||
# sudoers rule, or re-execed by require_root below — sudo's secure_path decides
|
||||
# where a bare helper resolves, and a dev link (etc/sudoers.d/omarchy-dev-path)
|
||||
# prepends a user-writable checkout bin/ to it. Every helper this script calls
|
||||
# by bare name (dirname, install, tee, rm, nmcli, systemctl, awk) is a system
|
||||
# tool, never an omarchy-* command, so pin PATH to trusted system directories
|
||||
# and keep root from resolving one out of that checkout. The unprivileged
|
||||
# wrapper phase keeps the caller's PATH so it can still find sudo/pkexec.
|
||||
if (( EUID == 0 )); then
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||
fi
|
||||
|
||||
NM_DNS_CONF=/etc/NetworkManager/conf.d/20-omarchy-dns.conf
|
||||
|
||||
provider_from_arg() {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Check that a git URL names a repository, not a transport helper
|
||||
# omarchy:args=<git-url>
|
||||
# omarchy:hidden=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# git picks a remote helper -- an executable it runs at clone time -- out of a URL
|
||||
# in exactly two shapes, and no others: `<helper>::<address>`, and
|
||||
# `<scheme>://<address>` for any scheme git does not handle itself. A single
|
||||
# colon is always scp-style ssh, and a bare path is always a path; neither can
|
||||
# reach a helper. So constraining those two shapes covers the whole surface.
|
||||
#
|
||||
# The `::` shape is refused outright, because no helper reachable that way is one
|
||||
# a theme or plugin URL has business naming, and `ext::` runs a shell command.
|
||||
# The `://` shape cannot be refused the same way, since it is also how every
|
||||
# legitimate URL arrives -- so it is allowlisted instead. The list is the
|
||||
# transports git still connects itself, `git+ssh` and `ssh+git` included: those
|
||||
# two are spelled like a helper but are read as plain ssh. `ext` and `fd` are
|
||||
# left out deliberately -- git ships a helper for each, and `ext` runs whatever
|
||||
# command the URL carries.
|
||||
TRANSPORTS=(ssh git git+ssh ssh+git http https ftp ftps file)
|
||||
|
||||
fail() {
|
||||
echo "omarchy-git-url-check: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
url="${1-}"
|
||||
|
||||
if [[ -z $url ]]; then
|
||||
fail "a git URL is required"
|
||||
fi
|
||||
|
||||
if [[ $url == -* || $url =~ ^[A-Za-z0-9][A-Za-z0-9+.-]*:: ]]; then
|
||||
fail "'$url' names a git option or transport helper, not a repository."
|
||||
fi
|
||||
|
||||
if [[ $url =~ ^([A-Za-z0-9][A-Za-z0-9+.-]*):// ]]; then
|
||||
scheme="${BASH_REMATCH[1]}"
|
||||
|
||||
for transport in "${TRANSPORTS[@]}"; do
|
||||
if [[ $scheme == "$transport" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
fail "'$url' names the '$scheme' transport, which Omarchy does not clone from."
|
||||
fi
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Match the Dell XPS 13 DX13260 that requires the sidecar amplifier workaround.
|
||||
|
||||
product_sku="${OMARCHY_DMI_PRODUCT_SKU:-/sys/class/dmi/id/product_sku}"
|
||||
|
||||
omarchy-hw-match "DX13260" &&
|
||||
grep -qix "0E53" "$product_sku" 2>/dev/null
|
||||
@@ -11,6 +11,14 @@ MONITOR_LUA="$HOME/.config/hypr/monitors.lua"
|
||||
|
||||
INTERNAL=$(omarchy-hyprland-monitor-laptop)
|
||||
|
||||
# INTERNAL is written into generated Lua and hyprctl eval/dispatch below, so a
|
||||
# name that is not a plain connector string could execute on the next reload.
|
||||
# Names come from hyprctl; a user-created headless output can carry anything.
|
||||
if [[ -n $INTERNAL && ! $INTERNAL =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
echo "Refusing unsafe internal monitor name" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
valid_scale() {
|
||||
[[ $1 =~ ^[0-9]+([.][0-9]+)?$ ]]
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@ off() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The name is written into generated Lua below, so only a plain connector
|
||||
# name may pass; anything else could execute on the next reload.
|
||||
if [[ ! $INTERNAL =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
omarchy-notification-send -g "Refusing unsafe monitor name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! omarchy-hyprland-monitor-external-active; then
|
||||
omarchy-notification-send -g "Can't disable the only active display"
|
||||
exit 1
|
||||
|
||||
@@ -22,6 +22,15 @@ on() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Both names are written into generated Lua below, so only plain connector
|
||||
# names may pass; a user-created headless output can carry any name.
|
||||
for output in "$INTERNAL" "$EXTERNAL"; do
|
||||
if [[ ! $output =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
omarchy-notification-send -g "Refusing unsafe monitor name"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
omarchy-hyprland-toggle $DISABLE_TOGGLE off
|
||||
|
||||
if omarchy-hyprland-toggle-disabled $TOGGLE; then
|
||||
|
||||
@@ -80,6 +80,14 @@ set_scale() {
|
||||
local width="$(echo "$monitor_info" | jq -r '.width')"
|
||||
local height="$(echo "$monitor_info" | jq -r '.height')"
|
||||
local refresh_rate="$(echo "$monitor_info" | jq -r '.refreshRate')"
|
||||
|
||||
# active_monitor is written into the Lua string eval'd below, so only a plain
|
||||
# connector name may pass; a hostile output name could execute otherwise.
|
||||
if [[ ! $active_monitor =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
echo "Refusing unsafe monitor name" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local new_scale="$(clean_scale "$requested_scale" "$width" "$height")"
|
||||
# GTK only honors integer GDK_SCALE values, so persist the nearest whole
|
||||
# factor even when the monitor scale itself is fractional.
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
set -e
|
||||
|
||||
setup_policy_directory() {
|
||||
sudo mkdir -p "$1"
|
||||
sudo chmod a+rw "$1"
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
|
||||
setup_chromium_policy_directory() {
|
||||
browser_policy_setup_dir "$1"
|
||||
}
|
||||
|
||||
announce_browser_installed() {
|
||||
@@ -23,13 +24,6 @@ copy_chromium_flags() {
|
||||
omarchy-install-chromium-ytdlp
|
||||
}
|
||||
|
||||
setup_firefox_preferences() {
|
||||
local distribution_dir="$1"
|
||||
|
||||
setup_policy_directory "$distribution_dir"
|
||||
sudo cp -f "$OMARCHY_PATH/default/firefox/policies.json" "$distribution_dir/policies.json"
|
||||
}
|
||||
|
||||
setup_firefox_wayland() {
|
||||
mkdir -p ~/.config/environment.d
|
||||
echo "MOZ_ENABLE_WAYLAND=1" > ~/.config/environment.d/omarchy-firefox-wayland.conf
|
||||
@@ -40,7 +34,7 @@ chromium)
|
||||
echo "Installing Chromium..."
|
||||
omarchy-pkg-add chromium
|
||||
|
||||
setup_policy_directory /etc/chromium/policies/managed
|
||||
setup_chromium_policy_directory /etc/chromium/policies/managed
|
||||
copy_chromium_flags ~/.config/chromium-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Chromium"
|
||||
@@ -49,7 +43,7 @@ chrome)
|
||||
echo "Installing Chrome..."
|
||||
omarchy-pkg-aur-add google-chrome || exit 1
|
||||
|
||||
setup_policy_directory /etc/opt/chrome/policies/managed
|
||||
setup_chromium_policy_directory /etc/opt/chrome/policies/managed
|
||||
copy_chromium_flags ~/.config/chrome-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Chrome"
|
||||
@@ -58,7 +52,7 @@ edge)
|
||||
echo "Installing Edge..."
|
||||
omarchy-pkg-aur-add microsoft-edge-stable-bin || exit 1
|
||||
|
||||
setup_policy_directory /etc/opt/edge/policies/managed
|
||||
setup_chromium_policy_directory /etc/opt/edge/policies/managed
|
||||
copy_chromium_flags ~/.config/microsoft-edge-stable-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Edge"
|
||||
@@ -67,7 +61,7 @@ brave)
|
||||
echo "Installing Brave..."
|
||||
omarchy-pkg-aur-add brave-bin || exit 1
|
||||
|
||||
setup_policy_directory /etc/brave/policies/managed
|
||||
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||
copy_chromium_flags ~/.config/brave-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Brave"
|
||||
@@ -76,7 +70,7 @@ brave-origin)
|
||||
echo "Installing Brave Origin..."
|
||||
omarchy-pkg-aur-add brave-origin-bin || exit 1
|
||||
|
||||
setup_policy_directory /etc/brave/policies/managed
|
||||
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||
copy_chromium_flags ~/.config/brave-origin-flags.conf
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Brave Origin"
|
||||
@@ -85,7 +79,7 @@ firefox)
|
||||
echo "Installing Firefox..."
|
||||
omarchy-pkg-add firefox || exit 1
|
||||
|
||||
setup_firefox_preferences /usr/lib/firefox/distribution
|
||||
browser_policy_setup_firefox_distribution /usr/lib/firefox/distribution
|
||||
setup_firefox_wayland
|
||||
announce_browser_installed "Firefox"
|
||||
;;
|
||||
@@ -93,7 +87,7 @@ zen)
|
||||
echo "Installing Zen..."
|
||||
omarchy-pkg-aur-add zen-browser-bin || exit 1
|
||||
|
||||
setup_firefox_preferences /opt/zen-browser/distribution
|
||||
browser_policy_setup_firefox_distribution /opt/zen-browser/distribution
|
||||
setup_firefox_wayland
|
||||
announce_browser_installed "Zen"
|
||||
;;
|
||||
|
||||
@@ -12,13 +12,10 @@ if gum confirm "Are you sure you want to restore all preinstalled web apps, TUI
|
||||
|
||||
# Mirrors the list in omarchy-remove-preinstalls; both track omarchy-base.packages
|
||||
if ! omarchy-pkg-add \
|
||||
aether \
|
||||
cliamp \
|
||||
libreoffice-fresh \
|
||||
xournalpp \
|
||||
pinta \
|
||||
obsidian \
|
||||
obs-studio \
|
||||
kdenlive \
|
||||
moonlight-qt \
|
||||
lazydocker \
|
||||
|
||||
@@ -10,4 +10,4 @@ echo "Enabling ONCE background service..."
|
||||
sudo systemctl enable --now once-background.service
|
||||
|
||||
echo -e "\nLaunching ONCE..."
|
||||
once
|
||||
sudo once
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
# no prompt. lazydocker needs the root-owned Docker socket, so when the group is
|
||||
# absent, gate that access behind a polkit prompt. If the user has opted into
|
||||
# sudoless Docker (omarchy-setup-security-sudoless-docker), the socket is already
|
||||
# reachable, so run lazydocker directly. pkexec sanitizes the environment, so
|
||||
# carry TERM through for the TUI to render and run lazydocker from root's PATH.
|
||||
if id -nG 2>/dev/null | grep -qw docker; then
|
||||
exec lazydocker
|
||||
else
|
||||
# reachable, so run lazydocker directly — omarchy-sudo-docker answers that for
|
||||
# this session, so the prompt stays until the reboot that grants the group.
|
||||
# pkexec sanitizes the environment, so carry TERM through for the TUI to render
|
||||
# and run lazydocker from root's PATH.
|
||||
if omarchy-sudo-docker; then
|
||||
exec pkexec /usr/bin/env TERM="${TERM:-xterm-256color}" lazydocker
|
||||
else
|
||||
exec lazydocker
|
||||
fi
|
||||
|
||||
@@ -338,10 +338,10 @@ static_bindings() {
|
||||
# for interchangeably belong on one row.
|
||||
alternative_chord_actions() {
|
||||
cat <<'ACTIONS'
|
||||
Close window
|
||||
Calculator
|
||||
Toggle scratchpad
|
||||
Move window to scratchpad
|
||||
关闭窗口
|
||||
计算器
|
||||
切换便签区
|
||||
窗口移入便签区
|
||||
ACTIONS
|
||||
}
|
||||
|
||||
@@ -442,58 +442,58 @@ prioritize_entries() {
|
||||
sub(/ \/ [^→]*→/, " →", line)
|
||||
|
||||
prio = 50
|
||||
if (match(line, /Keybindings/)) prio = 0
|
||||
if (match(line, /Omarchy menu/)) prio = 1
|
||||
if (match(line, /Terminal/)) prio = 2
|
||||
if (match(line, /Browser/) && !match(line, /Browser[[:space:]]*\(/) && !match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 3
|
||||
if (match(line, /File manager/) && !match(line, /File manager \(cwd\)/)) prio = 4
|
||||
if (match(line, /快捷键/)) prio = 0
|
||||
if (match(line, /Omarchy 菜单/)) prio = 1
|
||||
if (match(line, /终端/)) prio = 2
|
||||
if (match(line, /浏览器/) && !match(line, /浏览器(/) && !match(line, /SUPER SHIFT.*\+.*B.*→.*浏览器/)) prio = 3
|
||||
if (match(line, /文件管理器/) && !match(line, /文件管理器(当前目录)/)) prio = 4
|
||||
if (match(line, /Launch apps/)) prio = 5
|
||||
if (match(line, /System menu/)) prio = 6
|
||||
if (match(line, /Theme menu/)) prio = 7
|
||||
if (match(line, /Full screen/)) prio = 8
|
||||
if (match(line, /Full width/)) prio = 9
|
||||
if (match(line, /Close window/)) prio = 10
|
||||
if (match(line, /Close all windows/)) prio = 11
|
||||
if (match(line, /Lock system/)) prio = 12
|
||||
if (match(line, /Toggle window floating/)) prio = 13
|
||||
if (match(line, /Toggle window split/)) prio = 14
|
||||
if (match(line, /Pop window/)) prio = 15
|
||||
if (match(line, /Universal/)) prio = 16
|
||||
if (match(line, /Clipboard/)) prio = 17
|
||||
if (match(line, /系统菜单/)) prio = 6
|
||||
if (match(line, /主题菜单/)) prio = 7
|
||||
if (match(line, /全屏/)) prio = 8
|
||||
if (match(line, /全宽/)) prio = 9
|
||||
if (match(line, /关闭窗口/)) prio = 10
|
||||
if (match(line, /关闭所有窗口/)) prio = 11
|
||||
if (match(line, /锁定系统/)) prio = 12
|
||||
if (match(line, /切换窗口浮动/)) prio = 13
|
||||
if (match(line, /切换窗口分割/)) prio = 14
|
||||
if (match(line, /弹出窗口/)) prio = 15
|
||||
if (match(line, /通用/)) prio = 16
|
||||
if (match(line, /剪贴板/)) prio = 17
|
||||
if (match(line, /Audio controls/)) prio = 18
|
||||
if (match(line, /Bluetooth controls/)) prio = 19
|
||||
if (match(line, /Wifi controls/)) prio = 20
|
||||
if (match(line, /Emojis/)) prio = 21
|
||||
if (match(line, /Color picker/)) prio = 22
|
||||
if (match(line, /Screenshot/)) prio = 23
|
||||
if (match(line, /Screenrecording/)) prio = 24
|
||||
if (match(line, /表情/)) prio = 21
|
||||
if (match(line, /取色器/)) prio = 22
|
||||
if (match(line, /截图/)) prio = 23
|
||||
if (match(line, /录屏/)) prio = 24
|
||||
if (match(line, /Tmux/)) prio = 25
|
||||
if (match(line, /Herdr/)) prio = 26
|
||||
if (match(line, /SUPER SHIFT.*\+.*B.*→.*Browser/)) prio = 27
|
||||
if (match(line, /File manager \(cwd\)/)) prio = 28
|
||||
if (match(line, /(Switch|Next|Former|Previous).*workspace/)) prio = 29
|
||||
if (match(line, /Move window to workspace/)) prio = 30
|
||||
if (match(line, /Move window silently to workspace/)) prio = 31
|
||||
if (match(line, /Swap window/)) prio = 32
|
||||
if (match(line, /Focus/)) prio = 33
|
||||
if (match(line, /Move window$/)) prio = 34
|
||||
if (match(line, /Resize window/)) prio = 35
|
||||
if (match(line, /Expand window/)) prio = 36
|
||||
if (match(line, /Shrink window/)) prio = 37
|
||||
if (match(line, /scratchpad/)) prio = 38
|
||||
if (match(line, /notification/)) prio = 39
|
||||
if (match(line, /Toggle window transparency/)) prio = 40
|
||||
if (match(line, /Toggle workspace gaps/)) prio = 41
|
||||
if (match(line, /Toggle nightlight/)) prio = 42
|
||||
if (match(line, /Toggle locking/)) prio = 43
|
||||
if (match(line, /group/)) prio = 94
|
||||
if (match(line, /Scroll active workspace/)) prio = 95
|
||||
if (match(line, /SUPER SHIFT.*\+.*B.*→.*浏览器/)) prio = 27
|
||||
if (match(line, /文件管理器(当前目录)/)) prio = 28
|
||||
if (match(line, /(切换到|下一|上一|上次)工作区/)) prio = 29
|
||||
if (match(line, /窗口移到工作区/)) prio = 30
|
||||
if (match(line, /窗口静默移到工作区/)) prio = 31
|
||||
if (match(line, /互换/)) prio = 32
|
||||
if (match(line, /聚焦/)) prio = 33
|
||||
if (match(line, /移动窗口$/)) prio = 34
|
||||
if (match(line, /调整窗口大小/)) prio = 35
|
||||
if (match(line, /扩展窗口/)) prio = 36
|
||||
if (match(line, /收缩窗口/)) prio = 37
|
||||
if (match(line, /便签区/)) prio = 38
|
||||
if (match(line, /通知/)) prio = 39
|
||||
if (match(line, /切换窗口透明/)) prio = 40
|
||||
if (match(line, /切换窗口间距/)) prio = 41
|
||||
if (match(line, /切换夜间模式/)) prio = 42
|
||||
if (match(line, /切换空闲锁定/)) prio = 43
|
||||
if (match(line, /组内|分组/)) prio = 94
|
||||
if (match(line, /工作区向(前|后)滚动/)) prio = 95
|
||||
if (match(line, /Cycle to/)) prio = 96
|
||||
if (match(line, /Reveal active/)) prio = 97
|
||||
if (match(line, /置顶显示/)) prio = 97
|
||||
if (match(line, /Apple Display/)) prio = 98
|
||||
if (match(line, /XF86/)) prio = 99
|
||||
if (match(line, /Tmux keybindings/)) prio = 100
|
||||
if (match(line, /Herdr keybindings/)) prio = 101
|
||||
if (match(line, /Tmux 快捷键/)) prio = 100
|
||||
if (match(line, /Herdr 快捷键/)) prio = 101
|
||||
|
||||
# print "priority<TAB>record"
|
||||
printf "%d\t%s\n", prio, $0
|
||||
|
||||
@@ -96,6 +96,9 @@ while IFS=$'\t' read -r name file marker; do
|
||||
fi
|
||||
done < <(migration_entries)
|
||||
|
||||
# Here (not omarchy-update) so the first update from a pre-cn package runs them
|
||||
OMARCHY_PATH="$OMARCHY_PATH" omarchy-cn-migrate
|
||||
|
||||
# Clear a login-time notification the user left sitting there and then resolved
|
||||
# by running migrations some other way. The substring matches both the current
|
||||
# and legacy notification titles.
|
||||
|
||||
@@ -93,6 +93,13 @@ if [[ -z $url ]]; then
|
||||
[[ -n $url ]] || fail "a git URL is required"
|
||||
fi
|
||||
|
||||
# Refuse a URL that names a git option or a transport helper before cloning, so
|
||||
# an untrusted URL cannot run a command before the plugin is validated or
|
||||
# enabled. The check is shared with omarchy-theme-install and explains itself; a
|
||||
# missing checker leaves this non-zero, which refuses the URL rather than
|
||||
# cloning it.
|
||||
omarchy-git-url-check "$url" || exit 1
|
||||
|
||||
if (( ! ASSUME_YES )); then
|
||||
cat >&2 <<WARN
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ tips=(
|
||||
"Share files with phones and laptops using LocalSend"
|
||||
"Turn any website into an app with Install > Web App in the menu"
|
||||
"Super + Return opens a terminal, Super + Shift + Return the browser"
|
||||
"Edit images with Pinta, videos with Kdenlive, docs with LibreOffice"
|
||||
"Edit images with Pinta and videos with Kdenlive"
|
||||
"Super + Ctrl + Print grabs text off the screen with OCR"
|
||||
"Print takes a screenshot, Alt + Print records the screen"
|
||||
"Set a reminder with Super + Ctrl + R"
|
||||
@@ -742,6 +742,12 @@ create_user() {
|
||||
# for specific commands), and a duplicate grant is harmless.
|
||||
echo "%wheel ALL=(ALL:ALL) ALL" >/etc/sudoers.d/00-omarchy-wheel
|
||||
chmod 440 /etc/sudoers.d/00-omarchy-wheel
|
||||
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||
[[ -d $dir || -L $dir ]] || continue
|
||||
browser_policy_setup_dir "$dir"
|
||||
done
|
||||
}
|
||||
|
||||
install_authorized_keys() {
|
||||
|
||||
@@ -11,7 +11,7 @@ Usage: omarchy finalize user [--force] [--first-install]
|
||||
|
||||
Runs the per-user setup steps that /etc/skel can't seed:
|
||||
dev-aware skill symlinks, xdg-user-dirs + gtk bookmarks (need \$HOME),
|
||||
default browser/mailto, and install/user/all.sh.
|
||||
default browser, and install/user/all.sh.
|
||||
|
||||
For shipped configs see /etc/skel (new users) and omarchy-reinstall-configs
|
||||
(existing users explicitly resyncing).
|
||||
@@ -110,7 +110,6 @@ source "$OMARCHY_INSTALL/user/all.sh"
|
||||
|
||||
omarchy-refresh-applications
|
||||
env -u BROWSER xdg-settings set default-web-browser chromium.desktop
|
||||
xdg-mime default HEY.desktop x-scheme-handler/mailto
|
||||
|
||||
if (( first_install )); then
|
||||
mkdir -p "$state_dir/migrations"
|
||||
|
||||
@@ -18,13 +18,10 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w
|
||||
~/.local/bin/omp ~/.local/bin/ori ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk
|
||||
|
||||
omarchy-pkg-drop \
|
||||
aether \
|
||||
cliamp \
|
||||
libreoffice-fresh \
|
||||
xournalpp \
|
||||
pinta \
|
||||
obsidian \
|
||||
obs-studio \
|
||||
kdenlive \
|
||||
moonlight-qt \
|
||||
lazydocker \
|
||||
|
||||
@@ -24,9 +24,14 @@ echo -e "\e[32mRemoving FIDO2 device from authentication.\n\e[0m"
|
||||
|
||||
remove_pam_config
|
||||
|
||||
if [[ -d /etc/fido2 ]]; then
|
||||
authdir=/etc/fido2
|
||||
|
||||
# -d follows symlinks, so a dangling link at /etc/fido2 would survive this and
|
||||
# a later setup would install the authfile through it. rm -rf on a symlink
|
||||
# removes the link itself, never the directory it points at.
|
||||
if [[ -e $authdir || -L $authdir ]]; then
|
||||
echo "Removing FIDO2 configuration..."
|
||||
sudo rm -rf /etc/fido2
|
||||
sudo rm -rf "$authdir"
|
||||
fi
|
||||
|
||||
echo "Removing FIDO2 packages..."
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
|
||||
set -e
|
||||
|
||||
if ! id -nG "$USER" 2>/dev/null | grep -qw docker; then
|
||||
# Ask about the configured groups, not this session's: right after enabling,
|
||||
# sudoless Docker is on for the account even though the running session still
|
||||
# needs a prompt, and this command is what turns it back off.
|
||||
if omarchy-sudo-docker --configured; then
|
||||
echo "Sudoless Docker is not enabled: $USER is not in the docker group."
|
||||
exit 0
|
||||
fi
|
||||
@@ -13,13 +16,19 @@ fi
|
||||
echo "Removing $USER from the docker group..."
|
||||
sudo gpasswd -d "$USER" docker >/dev/null
|
||||
|
||||
# Group membership is fixed at login, so the running session keeps its docker
|
||||
# access until it ends. Flag a reboot so omarchy-update-restart prompts for one
|
||||
# (and the bar shows it pending); a plain log out and back in works too.
|
||||
# Group membership is only re-read by a fresh session, and in practice logging
|
||||
# out or newgrp isn't enough — only a reboot reliably applies it. Record it so a
|
||||
# later `omarchy update` still prompts (omarchy-update-restart reads this), then
|
||||
# offer to do it now.
|
||||
omarchy-state set reboot-required
|
||||
|
||||
echo ""
|
||||
echo "Sudoless Docker DISABLED. Reboot (or log out and back in) for the change to take effect."
|
||||
echo "Docker access now goes through a polkit/sudo prompt again: the Docker TUI"
|
||||
echo "(Super + Shift + D) and the Windows VM will ask when they need it, and the"
|
||||
echo "plain 'docker' CLI runs under sudo."
|
||||
echo "Sudoless Docker DISABLED. Docker access goes through a polkit/sudo prompt"
|
||||
echo "again: the Docker TUI (Super + Shift + D) and the Windows VM ask when they"
|
||||
echo "need it, and the plain 'docker' CLI runs under sudo. It takes effect after a reboot."
|
||||
echo ""
|
||||
# The migration reuses this command during 'omarchy update' and defers the
|
||||
# reboot to omarchy-update-restart, so it doesn't cut the update short.
|
||||
if [[ -z ${OMARCHY_DEFER_REBOOT:-} ]] && gum confirm "Reboot now to apply?"; then
|
||||
omarchy-system-reboot
|
||||
fi
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
|
||||
check_fido2_hardware() {
|
||||
@@ -50,13 +51,79 @@ if ! check_fido2_hardware; then
|
||||
fi
|
||||
|
||||
# Create the pamu2fcfg file
|
||||
if [[ ! -f /etc/fido2/fido2 ]]; then
|
||||
sudo mkdir -p /etc/fido2
|
||||
authdir=/etc/fido2
|
||||
authfile=/etc/fido2/fido2
|
||||
|
||||
# install -d follows a symlink here and applies the mode and ownership to
|
||||
# whatever it points at, so the credential would be staged and published inside
|
||||
# the link target and that directory reopened to root:root 755. This is the
|
||||
# threat omarchy-remove-security-fido2 already names on its side.
|
||||
if [[ -L $authdir || ( -e $authdir && ! -d $authdir ) ]]; then
|
||||
echo -e "\e[31m\n$authdir is not a FIDO2 configuration directory.\e[0m"
|
||||
echo "Run omarchy-remove-security-fido2 first, then set FIDO2 up again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# -f follows symlinks, so the already-registered check below reads a symlinked
|
||||
# authfile as a registration and leaves it in place, and is false for a
|
||||
# directory, so it tries to register over one. Only a regular file is a valid
|
||||
# pam_u2f authfile.
|
||||
if [[ -L $authfile || ( -e $authfile && ! -f $authfile ) ]]; then
|
||||
echo -e "\e[31m\n$authfile is not a FIDO2 registration file.\e[0m"
|
||||
echo "Run omarchy-remove-security-fido2 first, then set FIDO2 up again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f $authfile ]]; then
|
||||
sudo install -d -m 755 -o root -g root "$authdir"
|
||||
echo -e "\e[32m\nLet's setup your device by confirming on the device now.\e[0m"
|
||||
echo -e "Touch your FIDO2 key when it lights up...\n"
|
||||
|
||||
if pamu2fcfg >/tmp/fido2; then
|
||||
sudo mv /tmp/fido2 /etc/fido2/fido2
|
||||
# A unique sibling created by root cannot be replaced by another process
|
||||
# running as this user. Stream pamu2fcfg into it instead of asking root to
|
||||
# reopen a caller-owned path: an observed temporary name could otherwise be
|
||||
# replaced with a symlink before the privileged copy. The final rename is
|
||||
# atomic, and -T refuses a directory at the destination. Mode 644 keeps the
|
||||
# root-owned global authfile readable when pam_u2f uses openasuser; only root
|
||||
# can still rewrite it.
|
||||
stage=""
|
||||
|
||||
# mktemp's output is an operand for four privileged commands below, one of
|
||||
# them an rm. Take only the name this script asked for rather than whatever
|
||||
# came back on stdout.
|
||||
safe_stage_path() {
|
||||
local candidate=$1
|
||||
local prefix="$authfile.new."
|
||||
local suffix
|
||||
|
||||
[[ $candidate == "$prefix"* ]] || return 1
|
||||
suffix=${candidate#"$prefix"}
|
||||
[[ $suffix =~ ^[[:alnum:]]{6}$ ]]
|
||||
}
|
||||
|
||||
cleanup_stage() {
|
||||
local status=$?
|
||||
|
||||
if safe_stage_path "$stage"; then
|
||||
sudo rm -f -- "$stage" || true
|
||||
fi
|
||||
|
||||
return "$status"
|
||||
}
|
||||
|
||||
trap cleanup_stage EXIT
|
||||
stage=$(sudo mktemp "$authfile.new.XXXXXX")
|
||||
|
||||
if ! safe_stage_path "$stage" || [[ ! -f $stage || -L $stage ]]; then
|
||||
echo -e "\e[31m\nCould not create a safe staging file beside $authfile.\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pamu2fcfg | sudo tee "$stage" >/dev/null && [[ -s $stage ]]; then
|
||||
sudo chmod 644 "$stage"
|
||||
sudo mv -Tf "$stage" "$authfile"
|
||||
stage=""
|
||||
trap - EXIT
|
||||
echo -e "\e[32mFIDO2 device registered successfully!\e[0m"
|
||||
else
|
||||
echo -e "\e[31m\nFIDO2 registration failed. Please try again.\e[0m"
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
set -e
|
||||
|
||||
if id -nG "$USER" 2>/dev/null | grep -qw docker; then
|
||||
# Ask about the configured groups, not this session's: once enabled it stays
|
||||
# enabled for the account even before the reboot that lets this session use it.
|
||||
if ! omarchy-sudo-docker --configured; then
|
||||
echo "Sudoless Docker is already enabled: $USER is in the docker group."
|
||||
echo "To disable it again, run: omarchy-remove-security-sudoless-docker"
|
||||
exit 0
|
||||
@@ -28,14 +30,20 @@ echo ""
|
||||
|
||||
if gum confirm "Enable sudoless Docker? This gives anything running as you passwordless root."; then
|
||||
sudo usermod -aG docker "$USER"
|
||||
# Group membership is fixed at login, so docker won't be reachable without a
|
||||
# prompt until the session restarts. Flag a reboot so omarchy-update-restart
|
||||
# prompts for one (and the bar shows it pending).
|
||||
# A new docker group membership is only picked up by a fresh session, and in
|
||||
# practice logging out or newgrp isn't enough — only a reboot reliably applies
|
||||
# it. Record it so a later `omarchy update` still prompts
|
||||
# (omarchy-update-restart reads this), then offer to do it now.
|
||||
omarchy-state set reboot-required
|
||||
echo ""
|
||||
echo "Sudoless Docker ENABLED. Reboot, or log out and back in (or run 'newgrp docker'),"
|
||||
echo "for the new group membership to take effect."
|
||||
echo "To disable it again, run: omarchy-remove-security-sudoless-docker"
|
||||
echo "Sudoless Docker ENABLED. It takes effect after a reboot."
|
||||
echo "To disable it again: Setup > Security > Sudoless Docker."
|
||||
echo ""
|
||||
# The migration reuses this command during 'omarchy update' and defers the
|
||||
# reboot to omarchy-update-restart, so it doesn't cut the update short.
|
||||
if [[ -z ${OMARCHY_DEFER_REBOOT:-} ]] && gum confirm "Reboot now to apply?"; then
|
||||
omarchy-system-reboot
|
||||
fi
|
||||
else
|
||||
echo "Aborted. No changes made. Docker access still goes through a prompt."
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Succeed when Docker needs sudo, fail when it can be used directly
|
||||
# omarchy:args=[--configured]
|
||||
# omarchy:examples=omarchy-sudo-docker && echo "needs sudo" | omarchy-sudo-docker --configured
|
||||
# omarchy:hidden=true
|
||||
|
||||
# The docker group is root-equivalent, so Omarchy leaves users out of it by
|
||||
# default and reaches the daemon through a prompt instead. Everything that has
|
||||
# to make that choice asks here rather than testing group membership itself.
|
||||
#
|
||||
# Two questions, because they have different answers between toggling sudoless
|
||||
# Docker and the reboot that applies it (group membership is fixed when the
|
||||
# session is created):
|
||||
#
|
||||
# (default) Does Docker need sudo *right now*? Answered by whether this
|
||||
# process can actually reach the socket, which is what decides
|
||||
# if a command must elevate. Still true in the window after
|
||||
# sudoless Docker is enabled but before the reboot.
|
||||
# --configured Will it need sudo once the account's groups take effect?
|
||||
# Answered from the account's configured groups, so the menu
|
||||
# offers the toggle that can actually change state.
|
||||
#
|
||||
# Succeeds (exit 0) when sudo is needed, so it reads as `if omarchy-sudo-docker`.
|
||||
|
||||
DOCKER_SOCKET="${OMARCHY_DOCKER_SOCKET:-/var/run/docker.sock}"
|
||||
|
||||
case "${1:-}" in
|
||||
--configured)
|
||||
# An account in the docker group will not need sudo after the next login.
|
||||
id -nG "$USER" 2>/dev/null | grep -qw docker && exit 1
|
||||
exit 0
|
||||
;;
|
||||
"")
|
||||
# A socket we can write is a daemon we can drive without elevating. A missing
|
||||
# socket counts as needing sudo: reaching it means starting it as root anyway.
|
||||
[[ -w $DOCKER_SOCKET ]] && exit 1
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Usage: omarchy-sudo-docker [--configured]" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -16,14 +16,10 @@ if [[ -z $REPO_URL ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# git reads a leading dash as an option, and `<helper>::<address>` as a remote
|
||||
# helper to run. The helper name is a bare word at the very start, which is what
|
||||
# this matches; an scp-style IPv6 host such as git@[2001:db8::1]:org/repo.git
|
||||
# carries `::` too and must still clone.
|
||||
if [[ $REPO_URL == -* || $REPO_URL =~ ^[A-Za-z0-9][A-Za-z0-9+.-]*:: ]]; then
|
||||
echo "Error: '$REPO_URL' names a git option or transport helper, not a repository."
|
||||
exit 1
|
||||
fi
|
||||
# Refuse a URL that names a git option or a transport helper before cloning. The
|
||||
# check is shared with omarchy-plugin-add and explains itself; a missing checker
|
||||
# leaves this non-zero, which refuses the URL rather than cloning it.
|
||||
omarchy-git-url-check "$REPO_URL" || exit 1
|
||||
|
||||
THEMES_DIR="$HOME/.config/omarchy/themes"
|
||||
|
||||
|
||||
@@ -3,23 +3,15 @@
|
||||
# omarchy:summary=Apply the current theme color to Chromium, Chrome, Edge, and Brave
|
||||
# omarchy:hidden=true
|
||||
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
|
||||
CHROMIUM_THEME=$HOME/.local/state/omarchy/current/theme/chromium.theme
|
||||
THEME_HEX_COLOR=$BROWSER_POLICY_DEFAULT_COLOR
|
||||
|
||||
if [[ -f $CHROMIUM_THEME ]]; then
|
||||
THEME_RGB_COLOR=$(<$CHROMIUM_THEME)
|
||||
THEME_HEX_COLOR=$(printf '#%02x%02x%02x' ${THEME_RGB_COLOR//,/ })
|
||||
else
|
||||
# Use a default, neutral grey if theme doesn't have a color
|
||||
THEME_HEX_COLOR="#1c2027"
|
||||
THEME_HEX_COLOR=$(browser_policy_theme_hex "$(<$CHROMIUM_THEME)")
|
||||
fi
|
||||
|
||||
set_browser_policy() {
|
||||
local policy_dir="$1"
|
||||
|
||||
[[ -d $policy_dir ]] || return
|
||||
echo "{\"BrowserThemeColor\": \"$THEME_HEX_COLOR\", \"BrowserColorScheme\": \"device\"}" | tee "$policy_dir/color.json" >/dev/null
|
||||
}
|
||||
|
||||
refresh_running_browser() {
|
||||
local process="$1"
|
||||
local command="$2"
|
||||
@@ -30,17 +22,15 @@ refresh_running_browser() {
|
||||
fi
|
||||
}
|
||||
|
||||
set_browser_policy /etc/chromium/policies/managed
|
||||
failed=0
|
||||
omarchy-theme-set-browser-policy "${THEME_HEX_COLOR#\#}" || failed=1
|
||||
|
||||
refresh_running_browser chromium chromium
|
||||
|
||||
set_browser_policy /etc/opt/chrome/policies/managed
|
||||
refresh_running_browser chrome google-chrome-stable || refresh_running_browser chrome google-chrome
|
||||
|
||||
set_browser_policy /etc/opt/edge/policies/managed
|
||||
refresh_running_browser msedge microsoft-edge-stable
|
||||
|
||||
set_browser_policy /etc/brave/policies/managed
|
||||
refresh_running_browser brave brave
|
||||
# Match on the binary path: the running process is named plain "brave", and a
|
||||
# bare -f brave-origin pattern would also match the installer's own terminal.
|
||||
refresh_running_browser /opt/brave-origin-bin/ brave-origin -f
|
||||
|
||||
exit "$failed"
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Write the current theme color into the browser policy directories
|
||||
# omarchy:args=<rrggbb>
|
||||
# omarchy:hidden=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Whenever this runs as root — invoked directly through the passwordless
|
||||
# sudoers rule, or re-execed by require_root below — sudo's secure_path decides
|
||||
# where a bare helper resolves, and a dev link (etc/sudoers.d/omarchy-dev-path)
|
||||
# prepends a user-writable checkout bin/ to it. Every helper this script calls
|
||||
# by bare name (printf's builtin aside: install, mktemp, rm) is a system tool,
|
||||
# never an omarchy-* command, so pin PATH to trusted system directories and keep
|
||||
# root from resolving one out of that checkout. The unprivileged wrapper phase
|
||||
# keeps the caller's PATH so it can still find sudo/pkexec.
|
||||
if (( EUID == 0 )); then
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||
fi
|
||||
|
||||
# Enterprise policy trust roots. The list is fixed here rather than taken from
|
||||
# the caller: the caller chooses a color, never a path.
|
||||
POLICY_DIRS=(
|
||||
/etc/chromium/policies/managed
|
||||
/etc/opt/chrome/policies/managed
|
||||
/etc/opt/edge/policies/managed
|
||||
/etc/brave/policies/managed
|
||||
)
|
||||
|
||||
# The path etc/sudoers.d/omarchy-theme-browser names. The privileged half always
|
||||
# runs from there rather than from whichever copy was invoked, so the rule
|
||||
# matches even where $OMARCHY_PATH points at a checkout.
|
||||
PACKAGED_PATH=/usr/bin/omarchy-theme-set-browser-policy
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-theme-set-browser-policy <rrggbb>" >&2
|
||||
}
|
||||
|
||||
if (( $# != 1 )); then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
color="$1"
|
||||
|
||||
# Six lowercase hex digits is the whole of what this accepts. The leading "#"
|
||||
# is added when the JSON is written rather than passed in: "#" opens a comment
|
||||
# in sudoers, and keeping it out of argv lets the sudoers rule spell the
|
||||
# argument as a plain six-character glob.
|
||||
if [[ ! $color =~ ^[0-9a-f]{6}$ ]]; then
|
||||
echo "omarchy-theme-set-browser-policy: expected six lowercase hex digits, got '$color'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# True when sudo would run this exact command without stopping for a password.
|
||||
# `sudo -l` on its own reports whether a command is permitted, which the blanket
|
||||
# %wheel rule answers yes to for everything; the long listing prints the matched
|
||||
# entry's tags, so !authenticate is the grant in
|
||||
# etc/sudoers.d/omarchy-theme-browser and nothing else. Listing runs nothing
|
||||
# and, under -n, prompts for nothing.
|
||||
sudo_grants_passwordless() {
|
||||
sudo -n -l -l "$PACKAGED_PATH" "$@" 2>/dev/null | grep -q '!authenticate'
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if (( EUID == 0 )); then
|
||||
return
|
||||
elif [[ -t 0 ]] || sudo_grants_passwordless "$@"; then
|
||||
exec sudo "$PACKAGED_PATH" "$@"
|
||||
else
|
||||
exec pkexec "$PACKAGED_PATH" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
require_root "$color"
|
||||
|
||||
failed=0
|
||||
staged=""
|
||||
# Bash 5.3 makes the EXIT trap's last command decide the script's exit status,
|
||||
# so this handler must not end on a false test. Every successful run clears
|
||||
# staged, and a trailing `[[ -n $staged ]] && ...` would report that as failure.
|
||||
cleanup() {
|
||||
if [[ -n $staged ]]; then
|
||||
rm -f "$staged"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
for policy_dir in "${POLICY_DIRS[@]}"; do
|
||||
# Only browsers Omarchy has installed have a policy directory. Creating one
|
||||
# here would hand a browser a managed-policy root it does not otherwise have.
|
||||
[[ -d $policy_dir && ! -L $policy_dir ]] || continue
|
||||
|
||||
dest=$policy_dir/color.json
|
||||
staged=$(mktemp) || {
|
||||
failed=1
|
||||
continue
|
||||
}
|
||||
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
|
||||
|
||||
if [[ -L $dest || -d $dest ]]; then
|
||||
if ! rm -rf -- "$dest"; then
|
||||
rm -f "$staged"
|
||||
staged=""
|
||||
echo "omarchy-theme-set-browser-policy: cannot replace $dest" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! install -m 0644 -o root -g root -T "$staged" "$dest"; then
|
||||
rm -f "$staged"
|
||||
staged=""
|
||||
echo "omarchy-theme-set-browser-policy: cannot write $dest" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
|
||||
rm -f "$staged"
|
||||
staged=""
|
||||
done
|
||||
|
||||
exit "$failed"
|
||||
@@ -7,44 +7,70 @@
|
||||
KIND="${1:-}"
|
||||
ACTION="${2:-toggle}"
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-toggle-input-device <touchpad|touchscreen> [on|off|toggle]" >&2
|
||||
}
|
||||
|
||||
case "$KIND" in
|
||||
touchpad) LABEL="Touchpad" ICON="touchpad" ;;
|
||||
touchscreen) LABEL="Touchscreen" ICON="touch" ;;
|
||||
*)
|
||||
echo "Usage: omarchy-toggle-input-device <touchpad|touchscreen> [on|off|toggle]" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Hyprland sources this directory on reload, so the disabled state survives restarts
|
||||
STATE_FILE="$HOME/.local/state/omarchy/toggles/hypr/$KIND-disabled.lua"
|
||||
# The persisted disable is the device name stored as plain data; on every
|
||||
# reload default/hypr/disabled-input-device.lua reads it back and disables the
|
||||
# device. Names come from USB descriptors and must not be interpolated into
|
||||
# shell or Lua. The path is hardcoded to ~/.local/state like the sibling
|
||||
# toggle tools, so it keeps working when XDG_STATE_HOME diverges.
|
||||
NAME_FILE="$HOME/.local/state/omarchy/toggles/hypr/$KIND-disabled-name"
|
||||
|
||||
device="$("omarchy-hw-$KIND")"
|
||||
|
||||
if [[ -z $device ]]; then
|
||||
echo "No $KIND device found" >&2
|
||||
exit 1
|
||||
fi
|
||||
require_device() {
|
||||
if [[ -z $device ]]; then
|
||||
echo "No $KIND device found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $device == *[[:cntrl:]]* ]]; then
|
||||
echo "Invalid $KIND device name" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
apply_device() {
|
||||
local enabled=$1
|
||||
local quoted=${device//\\/\\\\}
|
||||
quoted=${quoted//\"/\\\"}
|
||||
hyprctl eval "hl.device({ name = \"$quoted\", enabled = $enabled })" >/dev/null
|
||||
}
|
||||
|
||||
enable() {
|
||||
hyprctl eval "hl.device({ name = \"$device\", enabled = true })" >/dev/null
|
||||
rm -f "$STATE_FILE"
|
||||
# Clear the persisted state before requiring a usable device, so a device
|
||||
# that stops reporting a valid name can never wedge the disable in place.
|
||||
rm -f "$NAME_FILE"
|
||||
require_device
|
||||
apply_device true
|
||||
omarchy-osd -i "$ICON" -m "$LABEL enabled"
|
||||
}
|
||||
|
||||
disable() {
|
||||
hyprctl eval "hl.device({ name = \"$device\", enabled = false })" >/dev/null
|
||||
mkdir -p "$(dirname "$STATE_FILE")"
|
||||
printf 'hl.device({ name = "%s", enabled = false })\n' "$device" >"$STATE_FILE"
|
||||
require_device
|
||||
apply_device false
|
||||
mkdir -p "$(dirname "$NAME_FILE")"
|
||||
printf '%s\n' "$device" >"$NAME_FILE"
|
||||
omarchy-osd -i "$ICON" -m "$LABEL disabled"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
on) enable ;;
|
||||
off) disable ;;
|
||||
toggle) if [[ -f $STATE_FILE ]]; then enable; else disable; fi ;;
|
||||
toggle) if [[ -f $NAME_FILE ]]; then enable; else disable; fi ;;
|
||||
*)
|
||||
echo "Usage: omarchy-toggle-input-device <touchpad|touchscreen> [on|off|toggle]" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
# omarchy:summary=Prompt for confirmation before starting an update
|
||||
|
||||
gum style --border normal --padding "1 2" \
|
||||
"Ready to update?" \
|
||||
"准备好更新了吗?" \
|
||||
"" \
|
||||
"• You cannot stop the update once you start!" \
|
||||
"• Make sure you're connected to power or have a full battery" \
|
||||
"• 更新一旦开始就不能中止!" \
|
||||
"• 请确保已接通电源或电量充足" \
|
||||
"" \
|
||||
"What's new: https://github.com/basecamp/omarchy/releases/latest"
|
||||
"更新内容: https://git.zacharyzhang.com/ZacharyZhang-NY/omarchycn/releases"
|
||||
|
||||
echo
|
||||
|
||||
if ! gum confirm "Continue with update?"; then
|
||||
echo "Update cancelled"
|
||||
if ! gum confirm "继续更新?"; then
|
||||
echo "已取消更新"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1312,7 +1312,21 @@ apply_system_transition() {
|
||||
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
||||
as_root gtk-update-icon-cache /usr/share/icons/Yaru >/dev/null 2>&1 || true
|
||||
|
||||
as_root install -d -m 0777 /etc/chromium/policies/managed
|
||||
local browser_policy_helper=/usr/share/omarchy/install/helpers/browser-policy.sh
|
||||
if ! as_root test -f "$browser_policy_helper"; then
|
||||
warn "$browser_policy_helper is unavailable; Chromium policy directories were not hardened."
|
||||
else
|
||||
as_root env OMARCHY_PATH=/usr/share/omarchy \
|
||||
bash -euo pipefail -c '
|
||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||
[[ $dir == "/etc/chromium/policies/managed" ]] && continue
|
||||
[[ -d $dir || -L $dir ]] || continue
|
||||
browser_policy_setup_dir "$dir"
|
||||
done
|
||||
'
|
||||
fi
|
||||
as_root install -d -m 0755 /usr/lib/chromium
|
||||
printf '%s\n' '{"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' | \
|
||||
as_root tee /usr/lib/chromium/initial_preferences >/dev/null
|
||||
@@ -2196,10 +2210,6 @@ fi
|
||||
if command -v xdg-settings >/dev/null 2>&1; then
|
||||
env -u BROWSER xdg-settings set default-web-browser chromium.desktop || true
|
||||
fi
|
||||
if command -v xdg-mime >/dev/null 2>&1; then
|
||||
xdg-mime default HEY.desktop x-scheme-handler/mailto || true
|
||||
fi
|
||||
|
||||
# This script is fetched from the branch and runs against whatever packaged
|
||||
# tree the channel currently serves, so the packaged tree can be older than the
|
||||
# script. The completion markers are written directly rather than through
|
||||
@@ -2306,6 +2316,11 @@ refresh_current_theme_after_upgrade() {
|
||||
# hooks because one of them runs `hyprctl reload`. Still poke terminal
|
||||
# emulators so the active upgrade terminal picks up generated theme files.
|
||||
run_as_user_omarchy omarchy-restart-terminal >/dev/null 2>&1 || true
|
||||
|
||||
# apply_system_transition purged user-owned color.json. Headless theme-set
|
||||
# skipped omarchy-theme-set-browser, so rewrite the colour here.
|
||||
run_as_user_omarchy omarchy-theme-set-browser >/dev/null 2>&1 ||
|
||||
warn "Could not apply browser theme colour. Run 'omarchy theme set \"$theme_name\"' after reboot if Chromium's theme looks stale."
|
||||
}
|
||||
|
||||
# Everything below mutates the system, so a non-zero exit from here on leaves a
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Open HEY webmail and translate mailto links
|
||||
# omarchy:args=[url]
|
||||
|
||||
url="$1"
|
||||
web_url="https://app.hey.com"
|
||||
|
||||
# Handle mailto: URLs
|
||||
if [[ $url =~ ^mailto: ]]; then
|
||||
email=$(echo "$url" | sed 's/mailto://')
|
||||
web_url="https://app.hey.com/messages/new?to=$email"
|
||||
fi
|
||||
|
||||
exec omarchy-launch-webapp "$web_url"
|
||||
@@ -13,6 +13,18 @@ safe_icon_name() {
|
||||
| sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//'
|
||||
}
|
||||
|
||||
require_plain_name() {
|
||||
# The name becomes a filename. A slash would turn it into directory levels, so
|
||||
# the launcher lands somewhere omarchy-webapp-remove cannot address and the app
|
||||
# is stuck in the launcher; a leading ../ leaves the applications directory
|
||||
# altogether. Refuse rather than silently renaming what the user typed -- most
|
||||
# often it is a URL entered in the name field.
|
||||
if [[ $1 == */* ]]; then
|
||||
echo "App name cannot contain '/': $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
icon_name_from_ref() {
|
||||
local ref="$1"
|
||||
local name
|
||||
@@ -68,6 +80,7 @@ fetch_site_icon() {
|
||||
if (( $# < 3 )); then
|
||||
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
||||
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
|
||||
require_plain_name "$APP_NAME"
|
||||
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
|
||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||
APP_URL="https://$APP_URL"
|
||||
@@ -104,6 +117,8 @@ if [[ -z $APP_NAME || -z $APP_URL ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_plain_name "$APP_NAME"
|
||||
|
||||
if [[ -z $ICON_REF ]]; then
|
||||
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
||||
mkdir -p "$ICON_DIR"
|
||||
@@ -132,8 +147,9 @@ fi
|
||||
EXEC_COMMAND="${CUSTOM_EXEC:-omarchy-launch-webapp $APP_URL}"
|
||||
|
||||
# Create application .desktop file
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
||||
mkdir -p "$(dirname "$DESKTOP_FILE")"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications"
|
||||
DESKTOP_FILE="$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
mkdir -p "$DESKTOP_DIR"
|
||||
|
||||
cat >"$DESKTOP_FILE" <<EOF
|
||||
[Desktop Entry]
|
||||
|
||||
@@ -9,14 +9,31 @@ ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
|
||||
OLD_ICON_DIR="$HOME/.local/share/applications/icons"
|
||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||
|
||||
if (( $# == 0 )); then
|
||||
# Find all web apps
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q '^Exec=.*\(omarchy-launch-webapp\|omarchy-webapp-handler\).*' "$file"; then
|
||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
||||
# Always index the launchers, so removal deletes the file that was found rather
|
||||
# than a path rebuilt from the displayed name. Installs predating the name
|
||||
# validation could nest the launcher inside directories, and those are exactly
|
||||
# the ones a reconstructed path cannot reach.
|
||||
WEB_APP_PATHS=()
|
||||
while IFS= read -r -d '' file; do
|
||||
if grep -q '^Exec=.*\(omarchy-launch-webapp\|omarchy-webapp-handler\).*' "$file"; then
|
||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||
WEB_APP_PATHS+=("$file")
|
||||
fi
|
||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0 2>/dev/null)
|
||||
|
||||
# The launcher matching a chosen name, or empty when nothing was indexed under
|
||||
# it (an app removed between the scan and the pick, say).
|
||||
path_for_web_app() {
|
||||
local wanted="$1" i
|
||||
for i in "${!WEB_APPS[@]}"; do
|
||||
if [[ ${WEB_APPS[$i]} == "$wanted" ]]; then
|
||||
printf '%s\n' "${WEB_APP_PATHS[$i]}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if (( $# == 0 )); then
|
||||
if ((${#WEB_APPS[@]})); then
|
||||
mapfile -t SORTED_WEB_APPS < <(printf '%s\n' "${WEB_APPS[@]}" | sort)
|
||||
APP_NAME=$(omarchy-menu-select "Select web app to remove" "${SORTED_WEB_APPS[@]}" -- --width 520 --maxheight 520)
|
||||
@@ -34,7 +51,8 @@ if [[ -z $APP_NAME ]]; then
|
||||
fi
|
||||
|
||||
icon_name=$(printf '%s\n' "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//')
|
||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
||||
desktop_file=$(path_for_web_app "$APP_NAME")
|
||||
rm -f "${desktop_file:-$DESKTOP_DIR/$APP_NAME.desktop}"
|
||||
rm -f "$ICON_DIR/$icon_name.png" "$ICON_DIR/$APP_NAME.png" "$OLD_ICON_DIR/$APP_NAME.png"
|
||||
|
||||
if [[ ${OMARCHY_REMOVE_NOTIFY:-true} != "false" ]]; then
|
||||
|
||||
@@ -31,8 +31,11 @@ CONTAINER="omarchy-windows"
|
||||
|
||||
# --- privilege helpers -------------------------------------------------------
|
||||
|
||||
# True when the user can reach the Docker socket directly (sudoless Docker on).
|
||||
in_docker_group() { id -nG 2>/dev/null | grep -qw docker; }
|
||||
# True when this session can reach the Docker socket directly (sudoless Docker
|
||||
# on and in effect). Asking about the socket rather than the configured groups
|
||||
# keeps the prompt in place through the window where sudoless Docker is enabled
|
||||
# but the reboot that grants the group has not happened yet.
|
||||
docker_needs_sudo() { omarchy-sudo-docker; }
|
||||
|
||||
# The command to hand pkexec for the privileged re-exec. pkexec runs whatever
|
||||
# executable it is given (after authorization) and only shows the path in the
|
||||
@@ -64,7 +67,7 @@ priv_target() {
|
||||
priv() {
|
||||
local action="$1"
|
||||
shift
|
||||
if [[ $action != write_compose ]] && in_docker_group; then
|
||||
if [[ $action != write_compose ]] && ! docker_needs_sudo; then
|
||||
"__priv_$action" "$@"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<!-- 每条 lang 规则成对出现:50-omarchy.conf 先以 assign 把 generic 名整体替换成
|
||||
Liberation Sans/Serif 与 JetBrainsMono Nerd Font(且先于本文件加载),
|
||||
所以除 generic 名外还必须测试替换后的具体家族,否则规则静默失效(Issue #4) -->
|
||||
|
||||
<!-- 日文/韩文标签内容保持原生字形(仅约束 generic 请求,避免波及未标签路径) -->
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>ja</string></test>
|
||||
<test name="family"><string>sans-serif</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK JP</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>ja</string></test>
|
||||
<test name="family"><string>Liberation Sans</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK JP</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>ko</string></test>
|
||||
<test name="family"><string>sans-serif</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK KR</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>ko</string></test>
|
||||
<test name="family"><string>Liberation Sans</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK KR</string></edit>
|
||||
</match>
|
||||
|
||||
<!-- 简体中文内容强制简体字形 -->
|
||||
<match target="pattern">
|
||||
@@ -19,16 +33,31 @@
|
||||
<test name="family"><string>sans-serif</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK SC</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>zh-cn</string></test>
|
||||
<test name="family"><string>Liberation Sans</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK SC</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>zh-cn</string></test>
|
||||
<test name="family"><string>serif</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Serif CJK SC</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>zh-cn</string></test>
|
||||
<test name="family"><string>Liberation Serif</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Serif CJK SC</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>zh-cn</string></test>
|
||||
<test name="family"><string>monospace</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans Mono CJK SC</string></edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<test name="lang" compare="contains"><string>zh-cn</string></test>
|
||||
<test name="family"><string>JetBrainsMono Nerd Font</string></test>
|
||||
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans Mono CJK SC</string></edit>
|
||||
</match>
|
||||
|
||||
<!-- 无语言标签的任意字体请求:汉字回退到 SC 而非 JP(Chromium/Electron 常见路径) -->
|
||||
<match target="pattern">
|
||||
|
||||
@@ -31,6 +31,21 @@ cn_ai_harness_field() {
|
||||
jq -re --arg h "$1" --arg f "$2" '.harnesses[$h][$f]' "$CN_AI_HARNESSES"
|
||||
}
|
||||
|
||||
# Providers with a fixed token (local servers) need no stored secret.
|
||||
# `// empty` keeps stdout clean when absent (bare -re would print "null").
|
||||
cn_ai_static_token() {
|
||||
jq -re --arg p "$1" '.providers[$p].static_token // empty' "$CN_AI_PROVIDERS"
|
||||
}
|
||||
|
||||
cn_ai_resolve_key() {
|
||||
cn_ai_static_token "$1" 2>/dev/null || omarchy-cn-ai-secret get "$1"
|
||||
}
|
||||
|
||||
# Dynamic-model providers list models at runtime, not in the registry
|
||||
cn_ai_models_dynamic() {
|
||||
jq -e --arg p "$1" '.providers[$p].models_dynamic == true' "$CN_AI_PROVIDERS" > /dev/null
|
||||
}
|
||||
|
||||
cn_ai_combo_level() {
|
||||
jq -r --arg h "$1" --arg p "$2" '.combos[$h][$p].level // "unsupported"' "$CN_AI_COMPAT"
|
||||
}
|
||||
@@ -68,7 +83,8 @@ cn_ai_render_env() {
|
||||
case "$harness" in
|
||||
claude-code)
|
||||
base=$(cn_ai_endpoint "$provider" anthropic)
|
||||
fast=$(cn_ai_fast_model "$provider")
|
||||
# Dynamic-model providers have no fast alias: every tier maps to the model
|
||||
fast=$(cn_ai_fast_model "$provider" 2>/dev/null || printf '%s' "$model")
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$base"
|
||||
# Docs vary between AUTH_TOKEN (deepseek/zai) and API_KEY (kimi): set both
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$key"
|
||||
|
||||
@@ -4,6 +4,22 @@
|
||||
CN_MIRRORS_JSON="$OMARCHY_PATH/cn/mirrors.json"
|
||||
CN_MIRRORLIST="/etc/pacman.d/mirrorlist"
|
||||
CN_PROFILE_FILE="$HOME/.config/omarchycn/mirror-profile"
|
||||
CN_OMARCHY_MIRROR="https://git.zacharyzhang.com/api/packages/ZacharyZhang-NY/arch/omarchy/x86_64"
|
||||
|
||||
# [omarchy] mirror line management (stable channel only); shared by the
|
||||
# rollout migration and doctor --fix so refresh-pacman resets stay healable
|
||||
cn_mirror_omarchy_repo_ok() {
|
||||
grep -qxF "Server = $CN_OMARCHY_MIRROR" "${OMARCHYCN_PACMAN_CONF:-/etc/pacman.conf}"
|
||||
}
|
||||
|
||||
cn_mirror_omarchy_repo_fix() {
|
||||
local conf="${OMARCHYCN_PACMAN_CONF:-/etc/pacman.conf}"
|
||||
# [omarchy] requires signed packages; the mirror re-signs with the registry key
|
||||
sudo pacman-key --add "$OMARCHY_PATH/cn/keys/omarchycn-registry.asc" > /dev/null || return 1
|
||||
sudo pacman-key --lsign-key 74DCF57ACD812B24D959F146BD386048867B33B4 > /dev/null || return 1
|
||||
cn_mirror_omarchy_repo_ok && return 0
|
||||
sudo sed -i "s|^Server = https://pkgs.omarchy.org/stable/|Server = $CN_OMARCHY_MIRROR\nServer = https://pkgs.omarchy.org/stable/|" "$conf"
|
||||
}
|
||||
|
||||
cn_mirror_ids() {
|
||||
jq -r '.mirrors[].id' "$CN_MIRRORS_JSON"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
echo "Trust the OmarchyCN registry key and put the [omarchy] mirror first"
|
||||
|
||||
# Only the stable channel is mirrored; edge/rc systems keep upstream directly
|
||||
source "$OMARCHY_PATH/cn/lib/mirror.sh"
|
||||
cn_mirror_omarchy_repo_fix
|
||||
@@ -0,0 +1,8 @@
|
||||
echo "Refresh the CJK fontconfig priority so zh-cn rules survive 50-omarchy.conf (Issue #4)"
|
||||
|
||||
# Only users who applied it before carry the broken copy
|
||||
conf="$HOME/.config/fontconfig/conf.d/64-omarchycn-cjk.conf"
|
||||
if [[ -f $conf ]]; then
|
||||
cp "$OMARCHY_PATH/cn/fontconfig/64-omarchycn-cjk.conf" "$conf"
|
||||
fc-cache -f > /dev/null
|
||||
fi
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"_verified": "2026-08-24. level=stable: adapter 渲染由 test/shell.d/omarchycn-ai-test.sh mock 回归覆盖; level=official-doc: 官方集成文档存在,native CLI 自管配置",
|
||||
"_verified": "2026-08-25. level=stable: adapter 渲染由 test/shell.d/omarchycn-ai-test.sh mock 回归覆盖; level=official-doc: 官方集成文档存在,native CLI 自管配置",
|
||||
"combos": {
|
||||
"claude-code": {
|
||||
"deepseek": {
|
||||
@@ -16,6 +16,16 @@
|
||||
"level": "stable",
|
||||
"doc": "https://docs.z.ai/devpack/tool/claude",
|
||||
"adapter": true
|
||||
},
|
||||
"minimax": {
|
||||
"level": "stable",
|
||||
"doc": "https://platform.minimax.io/docs/token-plan/claude-code",
|
||||
"adapter": true
|
||||
},
|
||||
"ollama": {
|
||||
"level": "stable",
|
||||
"doc": "https://docs.ollama.com/api/anthropic-compatibility",
|
||||
"adapter": true
|
||||
}
|
||||
},
|
||||
"codex": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"_verified": "2026-08-24",
|
||||
"_verified": "2026-08-25",
|
||||
"harnesses": {
|
||||
"claude-code": {
|
||||
"display_name": "Claude Code",
|
||||
@@ -25,7 +25,7 @@
|
||||
"kimi-code": {
|
||||
"display_name": "Kimi Code CLI",
|
||||
"command": "kimi",
|
||||
"install": "doc:https://www.kimi.com/code/docs/en/",
|
||||
"install": "omarchy-cn-kimi-install",
|
||||
"config_method": "native",
|
||||
"protocol": "native"
|
||||
},
|
||||
|
||||
@@ -1,44 +1,162 @@
|
||||
{
|
||||
"_verified": "2026-08-24, from official docs (see OmarchyCN PRD §27)",
|
||||
"_verified": "2026-08-27, from official docs (see OmarchyCN PRD §27; minimax: platform.minimax.io/docs/token-plan/claude-code; ollama: docs.ollama.com/api/anthropic-compatibility, fixed token \"ollama\", models are whatever is pulled locally)",
|
||||
"providers": {
|
||||
"deepseek": {
|
||||
"display_name": "DeepSeek",
|
||||
"auth": ["api_key"],
|
||||
"auth": [
|
||||
"api_key"
|
||||
],
|
||||
"key_url": "https://platform.deepseek.com/api_keys",
|
||||
"endpoints": {
|
||||
"anthropic": "https://api.deepseek.com/anthropic",
|
||||
"openai": "https://api.deepseek.com"
|
||||
},
|
||||
"models": [
|
||||
{ "id": "deepseek-v4-pro[1m]", "aliases": ["default-coding"], "capabilities": ["tools", "streaming", "reasoning"] },
|
||||
{ "id": "deepseek-v4-flash", "aliases": ["fast"], "capabilities": ["tools", "streaming"] }
|
||||
{
|
||||
"id": "deepseek-v4-pro[1m]",
|
||||
"aliases": [
|
||||
"default-coding"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming",
|
||||
"reasoning"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deepseek-v4-flash",
|
||||
"aliases": [
|
||||
"fast"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"kimi": {
|
||||
"display_name": "Kimi (Moonshot)",
|
||||
"auth": ["api_key"],
|
||||
"auth": [
|
||||
"api_key"
|
||||
],
|
||||
"key_url": "https://www.kimi.com/code",
|
||||
"endpoints": {
|
||||
"anthropic": "https://api.kimi.com/coding/",
|
||||
"openai": "https://api.moonshot.cn/v1"
|
||||
},
|
||||
"models": [
|
||||
{ "id": "kimi-for-coding", "aliases": ["default-coding"], "capabilities": ["tools", "streaming", "reasoning"] },
|
||||
{ "id": "kimi-for-coding-highspeed", "aliases": ["fast"], "capabilities": ["tools", "streaming"] },
|
||||
{ "id": "kimi-k2.6", "aliases": ["general"], "capabilities": ["tools", "streaming"], "protocol": "openai" }
|
||||
{
|
||||
"id": "kimi-for-coding",
|
||||
"aliases": [
|
||||
"default-coding"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming",
|
||||
"reasoning"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "kimi-for-coding-highspeed",
|
||||
"aliases": [
|
||||
"fast"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "kimi-k2.6",
|
||||
"aliases": [
|
||||
"general"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming"
|
||||
],
|
||||
"protocol": "openai"
|
||||
}
|
||||
]
|
||||
},
|
||||
"zai": {
|
||||
"display_name": "Z.AI / GLM",
|
||||
"auth": ["api_key"],
|
||||
"auth": [
|
||||
"api_key"
|
||||
],
|
||||
"key_url": "https://z.ai/manage-apikey/apikey-list",
|
||||
"endpoints": {
|
||||
"anthropic": "https://api.z.ai/api/anthropic"
|
||||
},
|
||||
"models": [
|
||||
{ "id": "glm-5.2[1m]", "aliases": ["default-coding"], "capabilities": ["tools", "streaming", "reasoning"] },
|
||||
{ "id": "GLM-5.3", "aliases": ["latest"], "capabilities": ["tools", "streaming", "reasoning"] },
|
||||
{ "id": "GLM-4.7", "aliases": ["fast"], "capabilities": ["tools", "streaming"] }
|
||||
{
|
||||
"id": "glm-5.2[1m]",
|
||||
"aliases": [
|
||||
"default-coding"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming",
|
||||
"reasoning"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "GLM-5.3",
|
||||
"aliases": [
|
||||
"latest"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming",
|
||||
"reasoning"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "GLM-4.7",
|
||||
"aliases": [
|
||||
"fast"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ollama": {
|
||||
"display_name": "Ollama 本地",
|
||||
"auth": [
|
||||
"none"
|
||||
],
|
||||
"static_token": "ollama",
|
||||
"endpoints": {
|
||||
"anthropic": "http://localhost:11434"
|
||||
},
|
||||
"models": [],
|
||||
"models_dynamic": true
|
||||
},
|
||||
"minimax": {
|
||||
"display_name": "MiniMax",
|
||||
"auth": [
|
||||
"api_key"
|
||||
],
|
||||
"key_url": "https://platform.minimaxi.com/user-center/payment/token-plan",
|
||||
"endpoints": {
|
||||
"anthropic": "https://api.minimaxi.com/anthropic"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "MiniMax-M3[1m]",
|
||||
"aliases": [
|
||||
"default-coding"
|
||||
],
|
||||
"capabilities": [
|
||||
"tools",
|
||||
"streaming",
|
||||
"reasoning"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
2
|
||||
8
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
--ozone-platform-hint=wayland
|
||||
--password-store=gnome-libsecret
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation
|
||||
--load-extension=/usr/share/omarchy/default/chromium/extensions/copy-url,/usr/share/omarchy/default/chromium/extensions/yt-dlp,/usr/share/omarchy/default/chromium/extensions/whatsapp-slim
|
||||
--load-extension=/usr/share/omarchy/default/chromium/extensions/copy-url,/usr/share/omarchy/default/chromium/extensions/yt-dlp
|
||||
|
||||
@@ -87,7 +87,38 @@ ambiguous, say so rather than assembling confidence out of guesswork.
|
||||
|
||||
**Leave the system as you found it.** Diagnosis reads; it does not fix, tidy, or
|
||||
reconfigure. The one thing to clean up is your own: delete the core you extracted
|
||||
above, which is a copy of the crashed process's memory.
|
||||
above, which is a copy of the crashed process's memory. The single change a
|
||||
diagnosis may make is the mute below, and only when the user asks for it.
|
||||
|
||||
## Offer to stop the notifications for this program
|
||||
|
||||
A crash you have explained often keeps happening anyway. Finish by offering to
|
||||
silence notifications for **that one program**, and never run it unprompted. Say
|
||||
how to lift it in the same breath, so it is not a one-way door.
|
||||
|
||||
```bash
|
||||
omarchy-crash-mute '<program>' # silence it
|
||||
omarchy-crash-mute '<program>' off # let it speak again
|
||||
omarchy-crash-mute # list what is muted
|
||||
```
|
||||
|
||||
Pass the `binary:` path from the crash facts, or the `process:` name where no
|
||||
binary was recorded; the command reduces either to the name the watcher keys on.
|
||||
A diagnosis run by hand from `omarchy agent crash <pid>` has neither, so take
|
||||
them from `coredumpctl info`. Prefer the binary: a process name is truncated to
|
||||
15 characters and a basename is not, so muting the truncated form matches
|
||||
nothing, forever, while looking like it worked.
|
||||
|
||||
Quote it. The name is whatever the crashed program's author called a file, and a
|
||||
single quote inside one closes yours and runs the rest as your shell.
|
||||
|
||||
The key is a bare name, so anything run through an interpreter is keyed as the
|
||||
interpreter: muting `python3.13` silences every Python program on the machine.
|
||||
Say so rather than quietly doing it.
|
||||
|
||||
None of this fixes anything, and a mute offered in place of a fix that was within
|
||||
reach is the wrong answer. For every program rather than one, the switch is
|
||||
_Trigger > Toggle > Crash Capture_.
|
||||
|
||||
## If it is an Omarchy bug
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ application/pdf=org.gnome.Evince.desktop
|
||||
|
||||
x-scheme-handler/http=chromium.desktop
|
||||
x-scheme-handler/https=chromium.desktop
|
||||
x-scheme-handler/mailto=HEY.desktop
|
||||
|
||||
video/mp4=mpv.desktop
|
||||
video/x-msvideo=mpv.desktop
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "WhatsApp Slim",
|
||||
"version": "1.1",
|
||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAszLhFYxjvKqx1VAORt3M6b0H1Kz03cmQ1MrUjJZ8gj3nhRcvufk6PS17yUIjEdKKmuj+1HuFVwOaHJQsACGJXwn2TeKQqZmZx/83yd7ydgabHXTsJbhy5O+Yunv+VA1B2IX/phcXpkH9bPv2PuC/bQ13nmmAvT2b+EMyq8EPQHOoSWhE4M4kIhLDczTmxfJX5X3tTxH66952DYuQWvGpIXLAUXkvIgap0qemW68/LymPaWzcg6g3oE/nZ7C0j7JE5bnqLFxqXNlA9Jt+Ss/1Y33QL3OSD9EEsrpgueS0bbqtkmFQ9/jGlruIL+qj0xe+CAtqbJ5ty9H/sqi01kjfyQIDAQAB",
|
||||
"description": "Make WhatsApp Web follow the system theme and collapse its chat list in narrow windows, this extension is installed by Omarchy",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://web.whatsapp.com/*"
|
||||
],
|
||||
"css": [
|
||||
"whatsapp.css"
|
||||
],
|
||||
"js": [
|
||||
"system-theme.js"
|
||||
],
|
||||
"run_at": "document_start"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
if (localStorage.getItem("system-theme-mode") !== "true") {
|
||||
localStorage.setItem("system-theme-mode", "true");
|
||||
location.reload();
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/* Collapse WhatsApp Web's chat list to a Signal-style avatar rail when the
|
||||
* window is too narrow to show both the full list and the conversation.
|
||||
* Leans on WhatsApp's stable structural ids (#side, #main, #pane-side) and
|
||||
* data-testid hooks rather than its obfuscated, frequently-churning class
|
||||
* names. */
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
/* The chat-list column (title header + #side) becomes an avatar rail */
|
||||
div:has(> div#side) {
|
||||
flex: 0 0 90px !important;
|
||||
min-width: 90px !important;
|
||||
max-width: 90px !important;
|
||||
}
|
||||
|
||||
/* Chat-list title bar, search, filter chips, and banner don't fit.
|
||||
* Scoped to the column wrapper: the left icon nav rail confusingly
|
||||
* shares the same chatlist-header testid and must survive. */
|
||||
div:has(> div#side) > header,
|
||||
#side > div:first-child,
|
||||
#side [role="tablist"],
|
||||
#side span[data-testid="chat-butterbar"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* The rail doesn't need a visible scrollbar, and horizontal overflow
|
||||
* from the collapsed rows must not wiggle */
|
||||
#pane-side {
|
||||
overflow-x: hidden !important;
|
||||
scrollbar-width: none !important;
|
||||
}
|
||||
|
||||
/* Chat rows: keep the avatar column, collapse the text/preview columns
|
||||
* without display:none so the unread badge can be resurrected */
|
||||
[data-testid="cell-frame-container"] {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
[data-testid="cell-frame-container"] > div:not(:first-child) {
|
||||
visibility: hidden !important;
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* Float the unread badge over the avatar's top-right corner */
|
||||
[data-testid="cell-frame-container"] span[aria-label*="unread"] {
|
||||
visibility: visible !important;
|
||||
position: absolute !important;
|
||||
top: 2px !important;
|
||||
left: 50px !important;
|
||||
}
|
||||
|
||||
/* The empty drawer overlay keeps the stock chat-list width, which
|
||||
* strands its divider border in the middle of the conversation */
|
||||
[data-testid="drawer-middle"] {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
/* Let the app shrink below WhatsApp's default minimum width */
|
||||
#app,
|
||||
#app .two {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,12 @@
|
||||
o.window(".*[Rr]esolve.*", {
|
||||
float = true,
|
||||
stay_focused = true,
|
||||
-- Prevent modal dialog pointer warps when focus follows the mouse.
|
||||
no_follow_mouse = true,
|
||||
tag = "-default-opacity",
|
||||
opacity = "1 1",
|
||||
})
|
||||
|
||||
o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve( Studio)? - .+$" }, { fullscreen = true })
|
||||
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve( Studio)? - .+|Project Manager)$" }, { stay_focused = false })
|
||||
-- Resolve exposes the Voiceover panel under the generic "Dialog" title.
|
||||
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve( Studio)? - .+|Project Manager|Preferences|Find Directory|Dialog)$" }, { stay_focused = false })
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
-- Essential application bindings.
|
||||
o.bind("SUPER + RETURN", "Terminal", { omarchy = "terminal" })
|
||||
o.bind("SUPER + SHIFT + RETURN", "Browser", { omarchy = "browser" })
|
||||
o.bind("SUPER + SHIFT + F", "File manager", { omarchy = "nautilus" })
|
||||
o.bind("SUPER + ALT + SHIFT + F", "File manager (cwd)", { omarchy = "nautilus-cwd" })
|
||||
o.bind("SUPER + SHIFT + B", "Browser", { omarchy = "browser" })
|
||||
o.bind("SUPER + SHIFT + ALT + B", "Browser (private)", { omarchy = "browser --private" })
|
||||
o.bind("SUPER + SHIFT + N", "Editor", { omarchy = "editor" })
|
||||
o.bind("SUPER + RETURN", "终端", { omarchy = "terminal" })
|
||||
o.bind("SUPER + SHIFT + RETURN", "浏览器", { omarchy = "browser" })
|
||||
o.bind("SUPER + SHIFT + F", "文件管理器", { omarchy = "nautilus" })
|
||||
o.bind("SUPER + ALT + SHIFT + F", "文件管理器(当前目录)", { omarchy = "nautilus-cwd" })
|
||||
o.bind("SUPER + SHIFT + B", "浏览器", { omarchy = "browser" })
|
||||
o.bind("SUPER + SHIFT + ALT + B", "浏览器(无痕)", { omarchy = "browser --private" })
|
||||
o.bind("SUPER + SHIFT + N", "编辑器", { omarchy = "editor" })
|
||||
|
||||
if o.preinstalled_bindings_enabled() then
|
||||
-- Bindings for preinstalled Omarchy applications, TUIs, and web apps.
|
||||
o.bind("SUPER + ALT + RETURN", "Tmux", { omarchy = "terminal-tmux" })
|
||||
o.bind("SUPER + CTRL + RETURN", "Herdr", { omarchy = "terminal-herdr" })
|
||||
o.bind("SUPER + SHIFT + M", "Music", { omarchy = "spotify" })
|
||||
o.bind("SUPER + SHIFT + ALT + M", "Music TUI", { tui = "cliamp", focus = true })
|
||||
o.bind("SUPER + SHIFT + M", "音乐", { omarchy = "spotify" })
|
||||
o.bind("SUPER + SHIFT + ALT + M", "音乐 TUI", { tui = "cliamp", focus = true })
|
||||
o.bind("SUPER + SHIFT + D", "Docker", { tui = "omarchy-launch-docker-tui" })
|
||||
o.bind("SUPER + SHIFT + G", "Signal", { omarchy = "signal" })
|
||||
o.bind("SUPER + SHIFT + O", "Obsidian", { launch = "obsidian", focus = "^obsidian$" })
|
||||
o.bind("SUPER + SHIFT + W", "Omawrite", { launch = "omawrite" })
|
||||
o.bind("SUPER + SHIFT + SLASH", "Passwords", { omarchy = "1password" })
|
||||
o.bind("SUPER + SHIFT + SLASH", "密码管理", { omarchy = "1password" })
|
||||
|
||||
o.bind("SUPER + SHIFT + A", "ChatGPT", { webapp = "https://chatgpt.com" })
|
||||
o.bind("SUPER + SHIFT + ALT + A", "Grok", { webapp = "https://grok.com" })
|
||||
o.bind("SUPER + SHIFT + C", "Calendar", { webapp = "https://app.hey.com/calendar/weeks/" })
|
||||
o.bind("SUPER + SHIFT + E", "Email", { webapp = "https://app.hey.com" })
|
||||
o.bind("SUPER + SHIFT + ALT + E", "New email", { webapp = "https://app.hey.com/messages/new?display=standalone&new_window=true" })
|
||||
o.bind("SUPER + SHIFT + Y", "YouTube", { webapp = "https://youtube.com/" })
|
||||
o.bind("SUPER + SHIFT + ALT + G", "WhatsApp", { webapp = "https://web.whatsapp.com/", focus = true })
|
||||
o.bind( "SUPER + SHIFT + CTRL + G", "Google Messages", { webapp = "https://messages.google.com/web/conversations", focus = true })
|
||||
o.bind("SUPER + SHIFT + P", "Google Photos", { webapp = "https://photos.google.com/", focus = true })
|
||||
o.bind("SUPER + SHIFT + S", "Google Maps", { webapp = "https://maps.google.com/", focus = true })
|
||||
o.bind("SUPER + SHIFT + X", "X", { webapp = "https://x.com/" })
|
||||
o.bind("SUPER + SHIFT + ALT + X", "X Post", { webapp = "https://x.com/compose/post" })
|
||||
end
|
||||
|
||||
@@ -42,7 +42,7 @@ local function universal_clipboard_shortcut(default_mods, default_key, terminal_
|
||||
end
|
||||
end
|
||||
|
||||
o.bind("SUPER + C", "Universal copy", universal_clipboard_shortcut("CTRL", "C", "CTRL", "Insert"))
|
||||
o.bind("SUPER + V", "Universal paste", universal_clipboard_shortcut("CTRL", "V", "SHIFT", "Insert"))
|
||||
o.bind("SUPER + X", "Universal cut", send_shortcut_once("CTRL", "X"))
|
||||
o.bind("SUPER + CTRL + V", "Clipboard manager", "omarchy-shell shell toggle omarchy.clipboard")
|
||||
o.bind("SUPER + C", "通用复制", universal_clipboard_shortcut("CTRL", "C", "CTRL", "Insert"))
|
||||
o.bind("SUPER + V", "通用粘贴", universal_clipboard_shortcut("CTRL", "V", "SHIFT", "Insert"))
|
||||
o.bind("SUPER + X", "通用剪切", send_shortcut_once("CTRL", "X"))
|
||||
o.bind("SUPER + CTRL + V", "剪贴板管理器", "omarchy-shell shell toggle omarchy.clipboard")
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
-- Volume, brightness, keyboard backlight, and touchpad controls.
|
||||
o.bind("XF86AudioRaiseVolume", "Volume up", "omarchy-audio-output-volume raise", { locked = true, repeating = true })
|
||||
o.bind("XF86AudioLowerVolume", "Volume down", "omarchy-audio-output-volume lower", { locked = true, repeating = true })
|
||||
o.bind("XF86AudioMute", "Mute", "omarchy-audio-output-volume mute-toggle", { locked = true })
|
||||
o.bind("XF86AudioMicMute", "Mute microphone", "omarchy-audio-input-mute", { locked = true })
|
||||
o.bind("XF86MonBrightnessUp", "Brightness up", "omarchy-brightness-display +5%", { locked = true, repeating = true })
|
||||
o.bind("XF86MonBrightnessDown", "Brightness down", "omarchy-brightness-display 5%-", { locked = true, repeating = true })
|
||||
o.bind("SHIFT + XF86MonBrightnessUp", "Brightness maximum", "omarchy-brightness-display 100%", { locked = true, repeating = true })
|
||||
o.bind("SHIFT + XF86MonBrightnessDown", "Brightness minimum", "omarchy-brightness-display 1%", { locked = true, repeating = true })
|
||||
o.bind("XF86KbdBrightnessUp", "Keyboard brightness up", "omarchy-brightness-keyboard up", { locked = true, repeating = true })
|
||||
o.bind("XF86KbdBrightnessDown", "Keyboard brightness down", "omarchy-brightness-keyboard down", { locked = true, repeating = true })
|
||||
o.bind("XF86KbdLightOnOff", "Keyboard backlight cycle", "omarchy-brightness-keyboard cycle", { locked = true })
|
||||
o.bind_toggle("XF86TouchpadToggle", "Toggle touchpad", "touchpad", { locked = true })
|
||||
o.bind("XF86TouchpadOn", "Enable touchpad", "omarchy-toggle-touchpad on", { locked = true })
|
||||
o.bind("XF86TouchpadOff", "Disable touchpad", "omarchy-toggle-touchpad off", { locked = true })
|
||||
o.bind("XF86AudioRaiseVolume", "提高音量", "omarchy-audio-output-volume raise", { locked = true, repeating = true })
|
||||
o.bind("XF86AudioLowerVolume", "降低音量", "omarchy-audio-output-volume lower", { locked = true, repeating = true })
|
||||
o.bind("XF86AudioMute", "静音", "omarchy-audio-output-volume mute-toggle", { locked = true })
|
||||
o.bind("XF86AudioMicMute", "麦克风静音", "omarchy-audio-input-mute", { locked = true })
|
||||
o.bind("XF86MonBrightnessUp", "提高亮度", "omarchy-brightness-display +5%", { locked = true, repeating = true })
|
||||
o.bind("XF86MonBrightnessDown", "降低亮度", "omarchy-brightness-display 5%-", { locked = true, repeating = true })
|
||||
o.bind("SHIFT + XF86MonBrightnessUp", "最大亮度", "omarchy-brightness-display 100%", { locked = true, repeating = true })
|
||||
o.bind("SHIFT + XF86MonBrightnessDown", "最小亮度", "omarchy-brightness-display 1%", { locked = true, repeating = true })
|
||||
o.bind("XF86KbdBrightnessUp", "键盘背光提高", "omarchy-brightness-keyboard up", { locked = true, repeating = true })
|
||||
o.bind("XF86KbdBrightnessDown", "键盘背光降低", "omarchy-brightness-keyboard down", { locked = true, repeating = true })
|
||||
o.bind("XF86KbdLightOnOff", "键盘背光循环", "omarchy-brightness-keyboard cycle", { locked = true })
|
||||
o.bind_toggle("XF86TouchpadToggle", "切换触控板", "touchpad", { locked = true })
|
||||
o.bind("XF86TouchpadOn", "启用触控板", "omarchy-toggle-touchpad on", { locked = true })
|
||||
o.bind("XF86TouchpadOff", "禁用触控板", "omarchy-toggle-touchpad off", { locked = true })
|
||||
|
||||
-- Precise volume and brightness controls.
|
||||
o.bind("ALT + XF86AudioRaiseVolume", "Volume up precise", "omarchy-audio-output-volume +1", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86AudioLowerVolume", "Volume down precise", "omarchy-audio-output-volume -1", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86MonBrightnessUp", "Brightness up precise", "omarchy-brightness-display +1%", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86MonBrightnessDown", "Brightness down precise", "omarchy-brightness-display 1%-", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86AudioRaiseVolume", "微调提高音量", "omarchy-audio-output-volume +1", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86AudioLowerVolume", "微调降低音量", "omarchy-audio-output-volume -1", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86MonBrightnessUp", "微调提高亮度", "omarchy-brightness-display +1%", { locked = true, repeating = true })
|
||||
o.bind("ALT + XF86MonBrightnessDown", "微调降低亮度", "omarchy-brightness-display 1%-", { locked = true, repeating = true })
|
||||
|
||||
-- Media controls.
|
||||
o.bind("XF86AudioNext", "Next track", "omarchy-shell media next", { locked = true })
|
||||
o.bind("ALT + XF86AudioPlay", "Next track", "omarchy-shell media next", { locked = true })
|
||||
o.bind("XF86AudioPause", "Pause", "omarchy-shell media playPause", { locked = true })
|
||||
o.bind("XF86AudioPlay", "Play", "omarchy-shell media playPause", { locked = true })
|
||||
o.bind("XF86AudioPrev", "Previous track", "omarchy-shell media previous", { locked = true })
|
||||
o.bind("ALT + SHIFT + XF86AudioPlay", "Previous track", "omarchy-shell media previous", { locked = true })
|
||||
o.bind("XF86Eject", "Eject media", "eject", { locked = true })
|
||||
o.bind("XF86AudioNext", "下一曲", "omarchy-shell media next", { locked = true })
|
||||
o.bind("ALT + XF86AudioPlay", "下一曲", "omarchy-shell media next", { locked = true })
|
||||
o.bind("XF86AudioPause", "暂停", "omarchy-shell media playPause", { locked = true })
|
||||
o.bind("XF86AudioPlay", "播放", "omarchy-shell media playPause", { locked = true })
|
||||
o.bind("XF86AudioPrev", "上一曲", "omarchy-shell media previous", { locked = true })
|
||||
o.bind("ALT + SHIFT + XF86AudioPlay", "上一曲", "omarchy-shell media previous", { locked = true })
|
||||
o.bind("XF86Eject", "弹出介质", "eject", { locked = true })
|
||||
|
||||
o.bind("SHIFT + XF86AudioMute", "Switch audio output", "omarchy-audio-output-switch", { locked = true })
|
||||
o.bind("SHIFT + XF86AudioPause", "Switch media source", "omarchy-audio-source-switch", { locked = true })
|
||||
o.bind("SHIFT + XF86AudioPlay", "Switch media source", "omarchy-audio-source-switch", { locked = true })
|
||||
o.bind("SHIFT + XF86AudioMute", "切换音频输出", "omarchy-audio-output-switch", { locked = true })
|
||||
o.bind("SHIFT + XF86AudioPause", "切换媒体源", "omarchy-audio-source-switch", { locked = true })
|
||||
o.bind("SHIFT + XF86AudioPlay", "切换媒体源", "omarchy-audio-source-switch", { locked = true })
|
||||
|
||||
@@ -1,98 +1,98 @@
|
||||
o.bind("SUPER + W", "Close window", hl.dsp.window.close())
|
||||
o.bind("SUPER + Q", "Close window", hl.dsp.window.close())
|
||||
o.bind("CTRL + ALT + DELETE", "Close all windows", "omarchy-hyprland-window-close-all")
|
||||
o.bind("SUPER + W", "关闭窗口", hl.dsp.window.close())
|
||||
o.bind("SUPER + Q", "关闭窗口", hl.dsp.window.close())
|
||||
o.bind("CTRL + ALT + DELETE", "关闭所有窗口", "omarchy-hyprland-window-close-all")
|
||||
|
||||
o.bind("SUPER + J", "Toggle window split", hl.dsp.layout("togglesplit"))
|
||||
o.bind("SUPER + P", "Pseudo window", hl.dsp.window.pseudo())
|
||||
o.bind("SUPER + T", "Toggle window floating/tiling", hl.dsp.window.float({ action = "toggle" }))
|
||||
o.bind("SUPER + F", "Full screen", hl.dsp.window.fullscreen({ mode = "fullscreen" }))
|
||||
o.bind("SUPER + CTRL + F", "Tiled full screen", "omarchy-hyprland-window-tiled-fullscreen-toggle")
|
||||
o.bind("SUPER + ALT + F", "Full width", hl.dsp.window.fullscreen({ mode = "maximized" }))
|
||||
o.bind("SUPER + O", "Pop window out (float & pin)", "omarchy-hyprland-window-pop")
|
||||
o.bind("SUPER + ALT + Home", "Save window width", "omarchy-hyprland-window-width save")
|
||||
o.bind("SUPER + Home", "Restore window width", "omarchy-hyprland-window-width restore")
|
||||
o.bind("SUPER + L", "Toggle workspace layout", "omarchy-hyprland-workspace-layout-toggle")
|
||||
o.bind("SUPER + J", "切换窗口分割", hl.dsp.layout("togglesplit"))
|
||||
o.bind("SUPER + P", "伪平铺窗口", hl.dsp.window.pseudo())
|
||||
o.bind("SUPER + T", "切换窗口浮动/平铺", hl.dsp.window.float({ action = "toggle" }))
|
||||
o.bind("SUPER + F", "全屏", hl.dsp.window.fullscreen({ mode = "fullscreen" }))
|
||||
o.bind("SUPER + CTRL + F", "平铺全屏", "omarchy-hyprland-window-tiled-fullscreen-toggle")
|
||||
o.bind("SUPER + ALT + F", "全宽", hl.dsp.window.fullscreen({ mode = "maximized" }))
|
||||
o.bind("SUPER + O", "弹出窗口(浮动并置顶)", "omarchy-hyprland-window-pop")
|
||||
o.bind("SUPER + ALT + Home", "保存窗口宽度", "omarchy-hyprland-window-width save")
|
||||
o.bind("SUPER + Home", "恢复窗口宽度", "omarchy-hyprland-window-width restore")
|
||||
o.bind("SUPER + L", "切换工作区布局", "omarchy-hyprland-workspace-layout-toggle")
|
||||
|
||||
o.bind("SUPER + LEFT", "Focus on left window", hl.dsp.focus({ direction = "l" }))
|
||||
o.bind("SUPER + RIGHT", "Focus on right window", hl.dsp.focus({ direction = "r" }))
|
||||
o.bind("SUPER + UP", "Focus on above window", hl.dsp.focus({ direction = "u" }))
|
||||
o.bind("SUPER + DOWN", "Focus on below window", hl.dsp.focus({ direction = "d" }))
|
||||
o.bind("SUPER + LEFT", "聚焦左侧窗口", hl.dsp.focus({ direction = "l" }))
|
||||
o.bind("SUPER + RIGHT", "聚焦右侧窗口", hl.dsp.focus({ direction = "r" }))
|
||||
o.bind("SUPER + UP", "聚焦上方窗口", hl.dsp.focus({ direction = "u" }))
|
||||
o.bind("SUPER + DOWN", "聚焦下方窗口", hl.dsp.focus({ direction = "d" }))
|
||||
|
||||
for workspace = 1, 10 do
|
||||
local key = "code:" .. tostring(workspace + 9)
|
||||
o.bind("SUPER + " .. key, "Switch to workspace " .. workspace, hl.dsp.focus({ workspace = tostring(workspace) }))
|
||||
o.bind("SUPER + SHIFT + " .. key, "Move window to workspace " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace) }))
|
||||
o.bind("SUPER + SHIFT + ALT + " .. key, "Move window silently to workspace " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace), follow = false }))
|
||||
o.bind("SUPER + " .. key, "切换到工作区 " .. workspace, hl.dsp.focus({ workspace = tostring(workspace) }))
|
||||
o.bind("SUPER + SHIFT + " .. key, "窗口移到工作区 " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace) }))
|
||||
o.bind("SUPER + SHIFT + ALT + " .. key, "窗口静默移到工作区 " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace), follow = false }))
|
||||
end
|
||||
|
||||
o.bind("SUPER + S", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
|
||||
o.bind("SUPER + ALT + S", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||
o.bind("SUPER + grave", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
|
||||
o.bind("SUPER + SHIFT + grave", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||
o.bind("SUPER + S", "切换便签区", hl.dsp.workspace.toggle_special("scratchpad"))
|
||||
o.bind("SUPER + ALT + S", "窗口移入便签区", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||
o.bind("SUPER + grave", "切换便签区", hl.dsp.workspace.toggle_special("scratchpad"))
|
||||
o.bind("SUPER + SHIFT + grave", "窗口移入便签区", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||
|
||||
o.bind("SUPER + TAB", "Next workspace", hl.dsp.focus({ workspace = "e+1" }))
|
||||
o.bind("SUPER + SHIFT + TAB", "Previous workspace", hl.dsp.focus({ workspace = "e-1" }))
|
||||
o.bind("SUPER + CTRL + TAB", "Former workspace", hl.dsp.focus({ workspace = "previous" }))
|
||||
o.bind("SUPER + TAB", "下一工作区", hl.dsp.focus({ workspace = "e+1" }))
|
||||
o.bind("SUPER + SHIFT + TAB", "上一工作区", hl.dsp.focus({ workspace = "e-1" }))
|
||||
o.bind("SUPER + CTRL + TAB", "上次工作区", hl.dsp.focus({ workspace = "previous" }))
|
||||
|
||||
o.bind("SUPER + SHIFT + ALT + LEFT", "Move workspace to left monitor", hl.dsp.workspace.move({ monitor = "l" }))
|
||||
o.bind("SUPER + SHIFT + ALT + RIGHT", "Move workspace to right monitor", hl.dsp.workspace.move({ monitor = "r" }))
|
||||
o.bind("SUPER + SHIFT + ALT + UP", "Move workspace to up monitor", hl.dsp.workspace.move({ monitor = "u" }))
|
||||
o.bind("SUPER + SHIFT + ALT + DOWN", "Move workspace to down monitor", hl.dsp.workspace.move({ monitor = "d" }))
|
||||
o.bind("SUPER + SHIFT + ALT + LEFT", "工作区移到左侧显示器", hl.dsp.workspace.move({ monitor = "l" }))
|
||||
o.bind("SUPER + SHIFT + ALT + RIGHT", "工作区移到右侧显示器", hl.dsp.workspace.move({ monitor = "r" }))
|
||||
o.bind("SUPER + SHIFT + ALT + UP", "工作区移到上方显示器", hl.dsp.workspace.move({ monitor = "u" }))
|
||||
o.bind("SUPER + SHIFT + ALT + DOWN", "工作区移到下方显示器", hl.dsp.workspace.move({ monitor = "d" }))
|
||||
|
||||
o.bind("SUPER + SHIFT + LEFT", "Swap window to the left", hl.dsp.window.swap({ direction = "l" }))
|
||||
o.bind("SUPER + SHIFT + RIGHT", "Swap window to the right", hl.dsp.window.swap({ direction = "r" }))
|
||||
o.bind("SUPER + SHIFT + UP", "Swap window up", hl.dsp.window.swap({ direction = "u" }))
|
||||
o.bind("SUPER + SHIFT + DOWN", "Swap window down", hl.dsp.window.swap({ direction = "d" }))
|
||||
o.bind("SUPER + SHIFT + LEFT", "窗口左移互换", hl.dsp.window.swap({ direction = "l" }))
|
||||
o.bind("SUPER + SHIFT + RIGHT", "窗口右移互换", hl.dsp.window.swap({ direction = "r" }))
|
||||
o.bind("SUPER + SHIFT + UP", "窗口上移互换", hl.dsp.window.swap({ direction = "u" }))
|
||||
o.bind("SUPER + SHIFT + DOWN", "窗口下移互换", hl.dsp.window.swap({ direction = "d" }))
|
||||
|
||||
o.bind("ALT + TAB", "Focus on next window", hl.dsp.window.cycle_next())
|
||||
o.bind("ALT + SHIFT + TAB", "Focus on previous window", hl.dsp.window.cycle_next({ next = false }))
|
||||
o.bind("ALT + TAB", "Reveal active window on top", hl.dsp.window.bring_to_top())
|
||||
o.bind("ALT + SHIFT + TAB", "Reveal active window on top", hl.dsp.window.bring_to_top())
|
||||
o.bind("ALT + TAB", "聚焦下一窗口", hl.dsp.window.cycle_next())
|
||||
o.bind("ALT + SHIFT + TAB", "聚焦上一窗口", hl.dsp.window.cycle_next({ next = false }))
|
||||
o.bind("ALT + TAB", "活动窗口置顶显示", hl.dsp.window.bring_to_top())
|
||||
o.bind("ALT + SHIFT + TAB", "活动窗口置顶显示", hl.dsp.window.bring_to_top())
|
||||
|
||||
o.bind("CTRL + ALT + TAB", "Focus on next monitor", hl.dsp.focus({ monitor = "+1" }))
|
||||
o.bind("CTRL + ALT + SHIFT + TAB", "Focus on previous monitor", hl.dsp.focus({ monitor = "-1" }))
|
||||
o.bind("CTRL + ALT + TAB", "聚焦下一显示器", hl.dsp.focus({ monitor = "+1" }))
|
||||
o.bind("CTRL + ALT + SHIFT + TAB", "聚焦上一显示器", hl.dsp.focus({ monitor = "-1" }))
|
||||
|
||||
o.bind("SUPER + code:20", "Expand window left", hl.dsp.window.resize({ x = -100, y = 0, relative = true }))
|
||||
o.bind("SUPER + code:21", "Shrink window left", hl.dsp.window.resize({ x = 100, y = 0, relative = true }))
|
||||
o.bind("SUPER + SHIFT + code:20", "Shrink window up", hl.dsp.window.resize({ x = 0, y = -100, relative = true }))
|
||||
o.bind("SUPER + SHIFT + code:21", "Expand window down", hl.dsp.window.resize({ x = 0, y = 100, relative = true }))
|
||||
o.bind("SUPER + code:20", "向左扩展窗口", hl.dsp.window.resize({ x = -100, y = 0, relative = true }))
|
||||
o.bind("SUPER + code:21", "向左收缩窗口", hl.dsp.window.resize({ x = 100, y = 0, relative = true }))
|
||||
o.bind("SUPER + SHIFT + code:20", "向上收缩窗口", hl.dsp.window.resize({ x = 0, y = -100, relative = true }))
|
||||
o.bind("SUPER + SHIFT + code:21", "向下扩展窗口", hl.dsp.window.resize({ x = 0, y = 100, relative = true }))
|
||||
|
||||
o.bind("SUPER + ALT + code:20", "Expand window left a little", hl.dsp.window.resize({ x = -25, y = 0, relative = true }))
|
||||
o.bind("SUPER + ALT + code:21", "Shrink window left a little", hl.dsp.window.resize({ x = 25, y = 0, relative = true }))
|
||||
o.bind("SUPER + SHIFT + ALT + code:20", "Shrink window up a little", hl.dsp.window.resize({ x = 0, y = -25, relative = true }))
|
||||
o.bind("SUPER + SHIFT + ALT + code:21", "Expand window down a little", hl.dsp.window.resize({ x = 0, y = 25, relative = true }))
|
||||
o.bind("SUPER + ALT + code:20", "向左略微扩展窗口", hl.dsp.window.resize({ x = -25, y = 0, relative = true }))
|
||||
o.bind("SUPER + ALT + code:21", "向左略微收缩窗口", hl.dsp.window.resize({ x = 25, y = 0, relative = true }))
|
||||
o.bind("SUPER + SHIFT + ALT + code:20", "向上略微收缩窗口", hl.dsp.window.resize({ x = 0, y = -25, relative = true }))
|
||||
o.bind("SUPER + SHIFT + ALT + code:21", "向下略微扩展窗口", hl.dsp.window.resize({ x = 0, y = 25, relative = true }))
|
||||
|
||||
o.bind("SUPER + CTRL + code:20", "Expand window left a lot", hl.dsp.window.resize({ x = -300, y = 0, relative = true }))
|
||||
o.bind("SUPER + CTRL + code:21", "Shrink window left a lot", hl.dsp.window.resize({ x = 300, y = 0, relative = true }))
|
||||
o.bind("SUPER + CTRL + SHIFT + code:20", "Shrink window up a lot", hl.dsp.window.resize({ x = 0, y = -300, relative = true }))
|
||||
o.bind("SUPER + CTRL + SHIFT + code:21", "Expand window down a lot", hl.dsp.window.resize({ x = 0, y = 300, relative = true }))
|
||||
o.bind("SUPER + CTRL + code:20", "向左大幅扩展窗口", hl.dsp.window.resize({ x = -300, y = 0, relative = true }))
|
||||
o.bind("SUPER + CTRL + code:21", "向左大幅收缩窗口", hl.dsp.window.resize({ x = 300, y = 0, relative = true }))
|
||||
o.bind("SUPER + CTRL + SHIFT + code:20", "向上大幅收缩窗口", hl.dsp.window.resize({ x = 0, y = -300, relative = true }))
|
||||
o.bind("SUPER + CTRL + SHIFT + code:21", "向下大幅扩展窗口", hl.dsp.window.resize({ x = 0, y = 300, relative = true }))
|
||||
|
||||
o.bind("SUPER + mouse_down", "Scroll active workspace forward", hl.dsp.focus({ workspace = "e+1" }))
|
||||
o.bind("SUPER + mouse_up", "Scroll active workspace backward", hl.dsp.focus({ workspace = "e-1" }))
|
||||
o.bind("SUPER + mouse_down", "工作区向前滚动", hl.dsp.focus({ workspace = "e+1" }))
|
||||
o.bind("SUPER + mouse_up", "工作区向后滚动", hl.dsp.focus({ workspace = "e-1" }))
|
||||
|
||||
o.bind("SUPER + mouse:272", "Move window", hl.dsp.window.drag(), { mouse = true })
|
||||
o.bind("SUPER + mouse:273", "Resize window", hl.dsp.window.resize(), { mouse = true })
|
||||
o.bind("SUPER + mouse:272", "移动窗口", hl.dsp.window.drag(), { mouse = true })
|
||||
o.bind("SUPER + mouse:273", "调整窗口大小", hl.dsp.window.resize(), { mouse = true })
|
||||
|
||||
o.bind("SUPER + G", "Toggle window grouping", hl.dsp.group.toggle())
|
||||
o.bind("SUPER + ALT + G", "Move active window out of group", hl.dsp.window.move({ out_of_group = true }))
|
||||
o.bind("SUPER + G", "切换窗口分组", hl.dsp.group.toggle())
|
||||
o.bind("SUPER + ALT + G", "将窗口移出分组", hl.dsp.window.move({ out_of_group = true }))
|
||||
|
||||
o.bind("SUPER + ALT + LEFT", "Move window to group on left", hl.dsp.window.move({ into_group = "l" }))
|
||||
o.bind("SUPER + ALT + RIGHT", "Move window to group on right", hl.dsp.window.move({ into_group = "r" }))
|
||||
o.bind("SUPER + ALT + UP", "Move window to group on top", hl.dsp.window.move({ into_group = "u" }))
|
||||
o.bind("SUPER + ALT + DOWN", "Move window to group on bottom", hl.dsp.window.move({ into_group = "d" }))
|
||||
o.bind("SUPER + ALT + LEFT", "窗口并入左侧分组", hl.dsp.window.move({ into_group = "l" }))
|
||||
o.bind("SUPER + ALT + RIGHT", "窗口并入右侧分组", hl.dsp.window.move({ into_group = "r" }))
|
||||
o.bind("SUPER + ALT + UP", "窗口并入上方分组", hl.dsp.window.move({ into_group = "u" }))
|
||||
o.bind("SUPER + ALT + DOWN", "窗口并入下方分组", hl.dsp.window.move({ into_group = "d" }))
|
||||
|
||||
o.bind("SUPER + ALT + TAB", "Next window in group", hl.dsp.group.next())
|
||||
o.bind("SUPER + ALT + SHIFT + TAB", "Previous window in group", hl.dsp.group.prev())
|
||||
o.bind("SUPER + ALT + TAB", "组内下一窗口", hl.dsp.group.next())
|
||||
o.bind("SUPER + ALT + SHIFT + TAB", "组内上一窗口", hl.dsp.group.prev())
|
||||
|
||||
o.bind("SUPER + CTRL + LEFT", "Move grouped window focus left", hl.dsp.group.prev())
|
||||
o.bind("SUPER + CTRL + RIGHT", "Move grouped window focus right", hl.dsp.group.next())
|
||||
o.bind("SUPER + CTRL + LEFT", "组内焦点左移", hl.dsp.group.prev())
|
||||
o.bind("SUPER + CTRL + RIGHT", "组内焦点右移", hl.dsp.group.next())
|
||||
|
||||
o.bind("SUPER + ALT + mouse_down", "Next window in group", hl.dsp.group.next())
|
||||
o.bind("SUPER + ALT + mouse_up", "Previous window in group", hl.dsp.group.prev())
|
||||
o.bind("SUPER + ALT + mouse_down", "组内下一窗口", hl.dsp.group.next())
|
||||
o.bind("SUPER + ALT + mouse_up", "组内上一窗口", hl.dsp.group.prev())
|
||||
|
||||
for index = 1, 5 do
|
||||
o.bind("SUPER + ALT + code:" .. tostring(index + 9), "Switch to group window " .. index, hl.dsp.group.active({ index = index }))
|
||||
o.bind("SUPER + ALT + code:" .. tostring(index + 9), "切换到组内窗口 " .. index, hl.dsp.group.active({ index = index }))
|
||||
end
|
||||
|
||||
o.bind("SUPER + SLASH", "Monitor scaling up", "omarchy-hyprland-monitor-scaling up")
|
||||
o.bind("SUPER + ALT + SLASH", "Monitor scaling down", "omarchy-hyprland-monitor-scaling down")
|
||||
o.bind("SUPER + SLASH", "显示器缩放增大", "omarchy-hyprland-monitor-scaling up")
|
||||
o.bind("SUPER + ALT + SLASH", "显示器缩放减小", "omarchy-hyprland-monitor-scaling down")
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
o.bind("SUPER + SPACE", "Omarchy menu", "omarchy-menu toggle")
|
||||
o.bind("SUPER + ALT + SPACE", "Apps menu", "omarchy-menu toggle apps")
|
||||
o.bind("SUPER + CTRL + E", "Emojis", "omarchy-shell shell toggle omarchy.emojis")
|
||||
o.bind("SUPER + CTRL + C", "Capture menu", "omarchy-menu toggle capture")
|
||||
o.bind("SUPER + CTRL + O", "Toggle menu", "omarchy-menu toggle toggle")
|
||||
o.bind("SUPER + CTRL + H", "Hardware menu", "omarchy-menu toggle hardware")
|
||||
o.bind("SUPER + SHIFT + code:201", "Omarchy menu", "omarchy-menu toggle root")
|
||||
o.bind("SUPER + ESCAPE", "System menu", "omarchy-menu toggle system")
|
||||
o.bind("XF86PowerOff", "Power menu", "omarchy-menu toggle system", { locked = true })
|
||||
o.bind("SUPER + K", "Keybindings", "omarchy-menu-keybindings")
|
||||
o.bind("SUPER + ALT + K", "Tmux keybindings", "omarchy-menu-tmux-keybindings")
|
||||
o.bind("SUPER + CTRL + K", "Herdr keybindings", "omarchy-menu-herdr-keybindings")
|
||||
o.bind("SUPER + CTRL + Q", "Calculator", "omacalc")
|
||||
o.bind("XF86Calculator", "Calculator", "omacalc")
|
||||
o.bind("SUPER + SPACE", "Omarchy 菜单", "omarchy-menu toggle")
|
||||
o.bind("SUPER + ALT + SPACE", "应用菜单", "omarchy-menu toggle apps")
|
||||
o.bind("SUPER + CTRL + E", "表情", "omarchy-shell shell toggle omarchy.emojis")
|
||||
o.bind("SUPER + CTRL + C", "截取菜单", "omarchy-menu toggle capture")
|
||||
o.bind("SUPER + CTRL + O", "切换菜单", "omarchy-menu toggle toggle")
|
||||
o.bind("SUPER + CTRL + H", "硬件菜单", "omarchy-menu toggle hardware")
|
||||
o.bind("SUPER + SHIFT + code:201", "Omarchy 菜单", "omarchy-menu toggle root")
|
||||
o.bind("SUPER + ESCAPE", "系统菜单", "omarchy-menu toggle system")
|
||||
o.bind("XF86PowerOff", "电源菜单", "omarchy-menu toggle system", { locked = true })
|
||||
o.bind("SUPER + K", "快捷键", "omarchy-menu-keybindings")
|
||||
o.bind("SUPER + ALT + K", "Tmux 快捷键", "omarchy-menu-tmux-keybindings")
|
||||
o.bind("SUPER + CTRL + K", "Herdr 快捷键", "omarchy-menu-herdr-keybindings")
|
||||
o.bind("SUPER + CTRL + Q", "计算器", "omacalc")
|
||||
o.bind("XF86Calculator", "计算器", "omacalc")
|
||||
|
||||
o.bind_toggle("SUPER + SHIFT + SPACE", "Toggle top bar", "bar")
|
||||
o.bind("SUPER + CTRL + SPACE", "Background switcher", "omarchy-menu toggle background")
|
||||
o.bind("SUPER + SHIFT + CTRL + SPACE", "Theme menu", "omarchy-menu toggle theme")
|
||||
o.bind("SUPER + BACKSPACE", "Toggle window transparency", "omarchy-hyprland-window-transparency-toggle")
|
||||
o.bind("SUPER + SHIFT + BACKSPACE", "Toggle window gaps", "omarchy-hyprland-window-gaps-toggle")
|
||||
o.bind("SUPER + CTRL + BACKSPACE", "Toggle single-window square aspect", "omarchy-hyprland-window-single-square-aspect-toggle")
|
||||
o.bind_toggle("SUPER + SHIFT + SPACE", "切换顶栏", "bar")
|
||||
o.bind("SUPER + CTRL + SPACE", "壁纸切换", "omarchy-menu toggle background")
|
||||
o.bind("SUPER + SHIFT + CTRL + SPACE", "主题菜单", "omarchy-menu toggle theme")
|
||||
o.bind("SUPER + BACKSPACE", "切换窗口透明", "omarchy-hyprland-window-transparency-toggle")
|
||||
o.bind("SUPER + SHIFT + BACKSPACE", "切换窗口间距", "omarchy-hyprland-window-gaps-toggle")
|
||||
o.bind("SUPER + CTRL + BACKSPACE", "切换单窗口方形比例", "omarchy-hyprland-window-single-square-aspect-toggle")
|
||||
|
||||
-- xkbcommon names the comma keysym "comma"; the upper-case "COMMA" does not match.
|
||||
o.bind("SUPER + comma", "Dismiss last notification", "omarchy-shell notifications dismissOne")
|
||||
o.bind("SUPER + SHIFT + comma", "Dismiss all notifications", "omarchy-shell notifications dismissAll")
|
||||
o.bind_toggle("SUPER + CTRL + comma", "Toggle silencing notifications", "notification-silencing")
|
||||
o.bind("SUPER + ALT + comma", "Invoke last notification", "omarchy-shell notifications invokeLast")
|
||||
o.bind("SUPER + SHIFT + ALT + comma", "Open notification history", "omarchy-shell notifications showHistory")
|
||||
o.bind("SUPER + comma", "清除最近通知", "omarchy-shell notifications dismissOne")
|
||||
o.bind("SUPER + SHIFT + comma", "清除全部通知", "omarchy-shell notifications dismissAll")
|
||||
o.bind_toggle("SUPER + CTRL + comma", "切换通知免打扰", "notification-silencing")
|
||||
o.bind("SUPER + ALT + comma", "打开最近通知", "omarchy-shell notifications invokeLast")
|
||||
o.bind("SUPER + SHIFT + ALT + comma", "通知历史", "omarchy-shell notifications showHistory")
|
||||
|
||||
o.bind_toggle("SUPER + CTRL + I", "Toggle locking on idle", "idle")
|
||||
o.bind_toggle("SUPER + CTRL + N", "Toggle nightlight", "nightlight")
|
||||
o.bind("SUPER + CTRL + Delete", "Toggle laptop display", "omarchy-hyprland-monitor-internal toggle")
|
||||
o.bind("SUPER + CTRL + ALT + Delete", "Toggle laptop display mirroring", "omarchy-hyprland-monitor-internal-mirror toggle")
|
||||
o.bind_toggle("SUPER + CTRL + I", "切换空闲锁定", "idle")
|
||||
o.bind_toggle("SUPER + CTRL + N", "切换夜间模式", "nightlight")
|
||||
o.bind("SUPER + CTRL + Delete", "切换笔记本屏幕", "omarchy-hyprland-monitor-internal toggle")
|
||||
o.bind("SUPER + CTRL + ALT + Delete", "切换笔记本镜像显示", "omarchy-hyprland-monitor-internal-mirror toggle")
|
||||
o.bind("switch:on:Lid Switch", nil, "omarchy-system-lid-close", { locked = true })
|
||||
o.bind("switch:off:Lid Switch", nil, "omarchy-hyprland-monitor-clamshell", { locked = true })
|
||||
|
||||
o.bind("PRINT", "Screenshot", "omarchy-capture-screenshot")
|
||||
o.bind("ALT + PRINT", "Screenrecording", "omarchy-capture-screenrecording --stop-recording || omarchy-menu toggle trigger.capture.screenrecord")
|
||||
o.bind("SUPER + ALT + code:34", "Make webcam overlay smaller", "omarchy-capture-webcam-resize smaller")
|
||||
o.bind("SUPER + ALT + code:35", "Make webcam overlay larger", "omarchy-capture-webcam-resize larger")
|
||||
o.bind("SUPER + PRINT", "Color picker", "pkill hyprpicker || hyprpicker -a")
|
||||
o.bind("SUPER + CTRL + PRINT", "Extract text (OCR) from screenshot", "omarchy-capture-text")
|
||||
o.bind("PRINT", "截图", "omarchy-capture-screenshot")
|
||||
o.bind("ALT + PRINT", "录屏", "omarchy-capture-screenrecording --stop-recording || omarchy-menu toggle trigger.capture.screenrecord")
|
||||
o.bind("SUPER + ALT + code:34", "缩小摄像头悬浮窗", "omarchy-capture-webcam-resize smaller")
|
||||
o.bind("SUPER + ALT + code:35", "放大摄像头悬浮窗", "omarchy-capture-webcam-resize larger")
|
||||
o.bind("SUPER + PRINT", "取色器", "pkill hyprpicker || hyprpicker -a")
|
||||
o.bind("SUPER + CTRL + PRINT", "截图取字(OCR)", "omarchy-capture-text")
|
||||
|
||||
-- Keyboard control for the slurp region picker (see omarchy-capture-region).
|
||||
-- The binds live exactly as long as a selection layer is on screen (slurp
|
||||
@@ -81,26 +81,26 @@ hl.on("layer.closed", function(layer)
|
||||
end
|
||||
end)
|
||||
|
||||
o.bind("SUPER + CTRL + S", "Share", "omarchy-menu toggle share")
|
||||
o.bind("SUPER + CTRL + S", "分享", "omarchy-menu toggle share")
|
||||
|
||||
o.bind("SUPER + CTRL + PERIOD", "Transcode", "omarchy-transcode")
|
||||
o.bind("SUPER + CTRL + PERIOD", "转码", "omarchy-transcode")
|
||||
|
||||
o.bind("SUPER + CTRL + R", "Set reminder", "omarchy-menu toggle reminder-set")
|
||||
o.bind("SUPER + CTRL + ALT + R", "Show reminders", "omarchy-reminder show")
|
||||
o.bind("SUPER + SHIFT + CTRL + R", "Clear reminders", "omarchy-reminder clear")
|
||||
o.bind("SUPER + CTRL + R", "设置提醒", "omarchy-menu toggle reminder-set")
|
||||
o.bind("SUPER + CTRL + ALT + R", "查看提醒", "omarchy-reminder show")
|
||||
o.bind("SUPER + SHIFT + CTRL + R", "清除提醒", "omarchy-reminder clear")
|
||||
|
||||
o.bind("SUPER + CTRL + ALT + T", "Show time", "omarchy-notification-time")
|
||||
o.bind("SUPER + CTRL + ALT + B", "Show battery remaining", "omarchy-notification-battery")
|
||||
o.bind("SUPER + CTRL + ALT + W", "Toggle weather", "omarchy-notification-weather")
|
||||
o.bind("SUPER + CTRL + ALT + T", "查看时间", "omarchy-notification-time")
|
||||
o.bind("SUPER + CTRL + ALT + B", "查看剩余电量", "omarchy-notification-battery")
|
||||
o.bind("SUPER + CTRL + ALT + W", "切换天气", "omarchy-notification-weather")
|
||||
|
||||
o.bind("SUPER + SHIFT + CTRL + A", "Agent", "omarchy-agent --pick")
|
||||
o.bind("SUPER + CTRL + A", "Audio", "omarchy-shell shell toggle omarchy.audio")
|
||||
o.bind("SUPER + CTRL + B", "Bluetooth", "omarchy-shell shell toggle omarchy.bluetooth")
|
||||
o.bind("SUPER + CTRL + D", "Display", "omarchy-shell shell toggle omarchy.monitor")
|
||||
o.bind("SUPER + CTRL + ALT + D", "Calendar", "omarchy-shell shell toggle omarchy.clock")
|
||||
o.bind("SUPER + CTRL + W", "Network", "omarchy-shell shell toggle omarchy.network")
|
||||
o.bind("SUPER + CTRL + P", "Power", "omarchy-shell shell toggle omarchy.power")
|
||||
o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
|
||||
o.bind("SUPER + CTRL + A", "音频", "omarchy-shell shell toggle omarchy.audio")
|
||||
o.bind("SUPER + CTRL + B", "蓝牙", "omarchy-shell shell toggle omarchy.bluetooth")
|
||||
o.bind("SUPER + CTRL + D", "显示", "omarchy-shell shell toggle omarchy.monitor")
|
||||
o.bind("SUPER + CTRL + ALT + D", "日历", "omarchy-shell shell toggle omarchy.clock")
|
||||
o.bind("SUPER + CTRL + W", "网络", "omarchy-shell shell toggle omarchy.network")
|
||||
o.bind("SUPER + CTRL + P", "电源", "omarchy-shell shell toggle omarchy.power")
|
||||
o.bind("SUPER + CTRL + T", "系统活动", { tui = "btop" })
|
||||
|
||||
-- The letters above name a panel; the numbers count them. 1 is the leftmost
|
||||
-- panel in the bar's right section, and a widget with no panel of its own (the
|
||||
@@ -109,18 +109,18 @@ o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
|
||||
for panel = 1, 9 do
|
||||
o.bind(
|
||||
"SUPER + CTRL + code:" .. tostring(panel + 9),
|
||||
"Bar panel " .. panel,
|
||||
"顶栏面板 " .. panel,
|
||||
"omarchy-shell -q shell togglePanelAt right " .. panel
|
||||
)
|
||||
end
|
||||
|
||||
o.bind("SUPER + CTRL + Z", "Zoom in", function()
|
||||
o.bind("SUPER + CTRL + Z", "放大", function()
|
||||
local zoom = hl.get_config("cursor.zoom_factor") or 1
|
||||
hl.config({ cursor = { zoom_factor = zoom + 1 } })
|
||||
end)
|
||||
|
||||
o.bind("SUPER + CTRL + ALT + Z", "Reset zoom", function()
|
||||
o.bind("SUPER + CTRL + ALT + Z", "重置缩放", function()
|
||||
hl.config({ cursor = { zoom_factor = 1 } })
|
||||
end)
|
||||
|
||||
o.bind("SUPER + CTRL + L", "Lock system", "omarchy-system-lock")
|
||||
o.bind("SUPER + CTRL + L", "锁定系统", "omarchy-system-lock")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
if o.cmd_present("voxtype") then
|
||||
o.bind("SUPER + CTRL + X", "Toggle dictation", "voxtype record toggle")
|
||||
o.bind("F9", "Start dictation (push-to-talk)", "voxtype record start")
|
||||
o.bind("F9", "Stop dictation (push-to-talk)", "voxtype record stop", { release = true })
|
||||
o.bind("SUPER + CTRL + X", "切换语音听写", "voxtype record toggle")
|
||||
o.bind("F9", "开始听写(按住说话)", "voxtype record start")
|
||||
o.bind("F9", "停止听写(按住说话)", "voxtype record stop", { release = true })
|
||||
end
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
-- Disable a Hyprland input device whose name was stored as data, not Lua.
|
||||
-- Device names come from USB descriptors and must never be loaded as code.
|
||||
|
||||
local paths = require("default.hypr.paths")
|
||||
|
||||
return function(kind)
|
||||
-- Hardcoded to ~/.local/state to match omarchy-toggle-input-device and the
|
||||
-- sibling bash toggle tools, which all write there regardless of
|
||||
-- XDG_STATE_HOME.
|
||||
local file = io.open(paths.home .. "/.local/state/omarchy/toggles/hypr/" .. kind .. "-disabled-name", "r")
|
||||
if not file then
|
||||
return
|
||||
end
|
||||
|
||||
local name = file:read("*l")
|
||||
file:close()
|
||||
|
||||
if name and name ~= "" then
|
||||
hl.device({ name = name, enabled = false })
|
||||
end
|
||||
end
|
||||
@@ -4,9 +4,19 @@
|
||||
|
||||
local home = os.getenv("HOME")
|
||||
|
||||
-- A variable that is set but empty means "unset" (XDG Base Directory spec);
|
||||
-- bash's ${VAR:-fallback} in the sibling tools treats it the same way.
|
||||
local function env_or(name, fallback)
|
||||
local value = os.getenv(name)
|
||||
if value == nil or value == "" then
|
||||
return fallback
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
return {
|
||||
home = home,
|
||||
config_home = os.getenv("XDG_CONFIG_HOME") or (home .. "/.config"),
|
||||
state_home = os.getenv("XDG_STATE_HOME") or (home .. "/.local/state"),
|
||||
omarchy_path = os.getenv("OMARCHY_PATH") or "/usr/share/omarchy",
|
||||
config_home = env_or("XDG_CONFIG_HOME", home .. "/.config"),
|
||||
state_home = env_or("XDG_STATE_HOME", home .. "/.local/state"),
|
||||
omarchy_path = env_or("OMARCHY_PATH", "/usr/share/omarchy"),
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
-- Pass a module prefix for normal package.path modules, e.g.
|
||||
-- require_all.files(paths.omarchy_path .. "/default/hypr/apps", "default.hypr.apps")
|
||||
-- Pass nil as the prefix when the directory itself has been added to package.path.
|
||||
-- Pass options.exclude as a set of base names (without ".lua") to skip; a legacy
|
||||
-- file that must never be loaded as code stays on disk for a migration to remove.
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -12,19 +14,23 @@ local function shell_quote(path)
|
||||
end
|
||||
|
||||
function M.files(dir, module_prefix, options)
|
||||
local exclude = options and options.exclude or {}
|
||||
local handle = io.popen("find " .. shell_quote(dir) .. " -maxdepth 1 -type f -name '*.lua' -printf '%f\\n' 2>/dev/null | sort")
|
||||
if handle then
|
||||
for filename in handle:lines() do
|
||||
local module = filename:gsub("%.lua$", "")
|
||||
if module_prefix then
|
||||
module = module_prefix .. "." .. module
|
||||
end
|
||||
local name = filename:gsub("%.lua$", "")
|
||||
if not exclude[name] then
|
||||
local module = name
|
||||
if module_prefix then
|
||||
module = module_prefix .. "." .. module
|
||||
end
|
||||
|
||||
if options and options.reload then
|
||||
package.loaded[module] = nil
|
||||
end
|
||||
if options and options.reload then
|
||||
package.loaded[module] = nil
|
||||
end
|
||||
|
||||
require(module)
|
||||
require(module)
|
||||
end
|
||||
end
|
||||
handle:close()
|
||||
end
|
||||
|
||||
@@ -4,6 +4,20 @@ local require_all = require("default.hypr.require_all")
|
||||
local toggles_dir = paths.state_home .. "/omarchy/toggles/hypr"
|
||||
package.path = toggles_dir .. "/?.lua;" .. package.path
|
||||
|
||||
require_all.files(toggles_dir, nil, { reload = true })
|
||||
-- touchpad-disabled.lua / touchscreen-disabled.lua were generated Lua in older
|
||||
-- versions and could carry an injected USB device name. They must never be loaded
|
||||
-- as code again: exclude them so a not-yet-migrated install cannot execute a
|
||||
-- leftover payload on reload. The migration recovers the name and deletes them.
|
||||
require_all.files(toggles_dir, nil, {
|
||||
reload = true,
|
||||
exclude = {
|
||||
["touchpad-disabled"] = true,
|
||||
["touchscreen-disabled"] = true,
|
||||
},
|
||||
})
|
||||
|
||||
local disabled_input_device = require("default.hypr.disabled-input-device")
|
||||
disabled_input_device("touchpad")
|
||||
disabled_input_device("touchscreen")
|
||||
|
||||
require("default.hypr.workspace-layouts")
|
||||
|
||||