Add a Setup > Region toggle with Chinese language and input method
`omarchy region china` switches the system language to zh_CN, installs the Rime Ice input method through Andy Stewart's rime-ice-installer, makes Rime the default with Ctrl+Space as the toggle, and swaps the Omarchy menu to Chinese labels through the user-extension overlay; `omarchy region world` puts the language and menu back and leaves the input method installed. mergeMenuSources now only overrides the fields an extension declares, as docs/menu.md documents, which the label-only overlay needs. Co-authored-by: Andy Stewart <lazycat.manatee@gmail.com>
This commit is contained in:
@@ -72,6 +72,7 @@ GROUP_DESCRIPTIONS[plymouth]="Plymouth boot theme management"
|
|||||||
GROUP_DESCRIPTIONS[power]="Power supply detection"
|
GROUP_DESCRIPTIONS[power]="Power supply detection"
|
||||||
GROUP_DESCRIPTIONS[powerprofiles]="Power profile management"
|
GROUP_DESCRIPTIONS[powerprofiles]="Power profile management"
|
||||||
GROUP_DESCRIPTIONS[refresh]="Reset config to defaults"
|
GROUP_DESCRIPTIONS[refresh]="Reset config to defaults"
|
||||||
|
GROUP_DESCRIPTIONS[region]="Region-specific sources and input method"
|
||||||
GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows"
|
GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows"
|
||||||
GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
|
GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
|
||||||
GROUP_DESCRIPTIONS[remove]="Removal workflows"
|
GROUP_DESCRIPTIONS[remove]="Removal workflows"
|
||||||
|
|||||||
Executable
+120
@@ -0,0 +1,120 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Show or set the region the machine is set up for
|
||||||
|
# omarchy:args=[world|china]
|
||||||
|
# omarchy:examples=omarchy region | omarchy region china
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
region_file="$HOME/.config/omarchy/region"
|
||||||
|
|
||||||
|
if (($# == 0)); then
|
||||||
|
region="World"
|
||||||
|
if [[ -f $region_file ]] && read -r stored <"$region_file"; then
|
||||||
|
region=$stored
|
||||||
|
fi
|
||||||
|
echo "$region"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (($# > 1)); then
|
||||||
|
echo "Usage: omarchy-region <world|china>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
world | World) region="World" ;;
|
||||||
|
china | China) region="China" ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: omarchy-region <world|china>"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
menu_overlay="$HOME/.config/omarchy/extensions/omarchy-menu.jsonc"
|
||||||
|
menu_sample="$OMARCHY_PATH/config/omarchy/extensions/omarchy-menu.jsonc"
|
||||||
|
overlay_marker="// Omarchy region managed"
|
||||||
|
|
||||||
|
if [[ $region == "China" ]]; then
|
||||||
|
# The stock machine carries the commented sample extension; a menu extension
|
||||||
|
# someone wrote by hand refuses the switch instead of being overwritten.
|
||||||
|
if [[ -s $menu_overlay ]] && ! grep -qFx "$overlay_marker" "$menu_overlay" &&
|
||||||
|
! cmp -s "$menu_overlay" "$menu_sample"; then
|
||||||
|
echo "menu: $menu_overlay has content Omarchy does not manage" >&2
|
||||||
|
echo "Resolve it by hand, then rerun: omarchy region china" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${menu_overlay%/*}"
|
||||||
|
cp "$OMARCHY_PATH/default/omarchy/omarchy-menu.zh-cn.jsonc" "$menu_overlay"
|
||||||
|
echo "menu -> Chinese labels"
|
||||||
|
|
||||||
|
# localectl writes /etc/locale.conf; the session picks it up at next login.
|
||||||
|
if ! grep -qsFx 'LANG=zh_CN.UTF-8' /etc/locale.conf; then
|
||||||
|
sudo sed -i 's/^#\(zh_CN\.UTF-8 UTF-8\)/\1/' /etc/locale.gen
|
||||||
|
sudo locale-gen
|
||||||
|
sudo localectl set-locale LANG=zh_CN.UTF-8
|
||||||
|
fi
|
||||||
|
echo "language -> zh_CN.UTF-8 (from the next login)"
|
||||||
|
else
|
||||||
|
if [[ -f $menu_overlay ]] && grep -qFx "$overlay_marker" "$menu_overlay"; then
|
||||||
|
cp "$menu_sample" "$menu_overlay"
|
||||||
|
echo "menu -> stock labels"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -qsFx 'LANG=zh_CN.UTF-8' /etc/locale.conf; then
|
||||||
|
sudo localectl set-locale LANG=en_US.UTF-8
|
||||||
|
echo "language -> en_US.UTF-8 (from the next login)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rime Ice's TUI drives its own sudo and fcitx5 restart; the schema file is
|
||||||
|
# what a finished install leaves behind. A fresh install has no repo databases
|
||||||
|
# until its first sync, and both pkg-add and the installer's pacman need them.
|
||||||
|
if [[ $region == "China" && ! -f $HOME/.local/share/fcitx5/rime/rime_ice.schema.yaml ]]; then
|
||||||
|
sudo pacman -Sy
|
||||||
|
omarchy-pkg-add rime-ice-installer
|
||||||
|
rime-ice-installer
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rime first makes Chinese the active default; keyboard-us stays one
|
||||||
|
# Ctrl+Space away, pinned explicitly rather than left to fcitx5's default.
|
||||||
|
if [[ $region == "China" ]]; then
|
||||||
|
fcitx_dir="$HOME/.config/fcitx5"
|
||||||
|
mkdir -p "$fcitx_dir"
|
||||||
|
cat >"$fcitx_dir/profile" <<EOF
|
||||||
|
[Groups/0]
|
||||||
|
Name=Default
|
||||||
|
Default Layout=us
|
||||||
|
DefaultIM=rime
|
||||||
|
|
||||||
|
[Groups/0/Items/0]
|
||||||
|
Name=rime
|
||||||
|
Layout=
|
||||||
|
|
||||||
|
[Groups/0/Items/1]
|
||||||
|
Name=keyboard-us
|
||||||
|
Layout=
|
||||||
|
|
||||||
|
[GroupOrder]
|
||||||
|
0=Default
|
||||||
|
EOF
|
||||||
|
|
||||||
|
touch "$fcitx_dir/config"
|
||||||
|
awk '
|
||||||
|
/^\[Hotkey\/TriggerKeys\]$/ { skip = 1; next }
|
||||||
|
/^\[/ { skip = 0 }
|
||||||
|
!skip { print }
|
||||||
|
' "$fcitx_dir/config" >"$fcitx_dir/config.tmp"
|
||||||
|
printf '[Hotkey/TriggerKeys]\n0=Control+space\n' >>"$fcitx_dir/config.tmp"
|
||||||
|
mv "$fcitx_dir/config.tmp" "$fcitx_dir/config"
|
||||||
|
echo "input method -> Rime Ice by default, Ctrl+Space toggles"
|
||||||
|
|
||||||
|
if systemctl --user is-active omarchy-fcitx5.service >/dev/null 2>&1; then
|
||||||
|
systemctl --user restart omarchy-fcitx5.service
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${region_file%/*}"
|
||||||
|
printf '%s\n' "$region" >"$region_file"
|
||||||
|
echo "Region set to $region"
|
||||||
@@ -167,6 +167,9 @@
|
|||||||
"setup.default.editor.helix": {"icon":"","label":"Helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
|
"setup.default.editor.helix": {"icon":"","label":"Helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
|
||||||
"setup.default.editor.vim": {"icon":"","label":"Vim","checked":"[[ \"$(omarchy-default-editor)\" == \"vim\" ]]","action":"omarchy-default-editor vim"},
|
"setup.default.editor.vim": {"icon":"","label":"Vim","checked":"[[ \"$(omarchy-default-editor)\" == \"vim\" ]]","action":"omarchy-default-editor vim"},
|
||||||
"setup.default.editor.emacs": {"icon":"","label":"Emacs","checked":"[[ \"$(omarchy-default-editor)\" == \"emacs\" ]]","action":"omarchy-default-editor emacs"},
|
"setup.default.editor.emacs": {"icon":"","label":"Emacs","checked":"[[ \"$(omarchy-default-editor)\" == \"emacs\" ]]","action":"omarchy-default-editor emacs"},
|
||||||
|
"setup.region": {"icon":"","label":"Region"},
|
||||||
|
"setup.region.world": {"icon":"","label":"World","checked":"[[ \"$(omarchy-region)\" == \"World\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-region world'"},
|
||||||
|
"setup.region.china": {"icon":"","label":"China","checked":"[[ \"$(omarchy-region)\" == \"China\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-region china'"},
|
||||||
"setup.plugin": {"icon":"","label":"Plugins","aliases":["plugin","plugins"]},
|
"setup.plugin": {"icon":"","label":"Plugins","aliases":["plugin","plugins"]},
|
||||||
"setup.plugin.enable": {"icon":"","label":"Enable Plugin","action":"omarchy-menu-plugin enable"},
|
"setup.plugin.enable": {"icon":"","label":"Enable Plugin","action":"omarchy-menu-plugin enable"},
|
||||||
"setup.plugin.disable": {"icon":"","label":"Disable Plugin","action":"omarchy-menu-plugin disable"},
|
"setup.plugin.disable": {"icon":"","label":"Disable Plugin","action":"omarchy-menu-plugin disable"},
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
// Omarchy region managed
|
||||||
|
// Chinese labels for the shipped menu. `omarchy region china` installs this
|
||||||
|
// as the user menu extension; ids keep their actions and icons.
|
||||||
|
{
|
||||||
|
"apps": {"label":"应用"},
|
||||||
|
"learn": {"label":"学习"},
|
||||||
|
"trigger": {"label":"触发"},
|
||||||
|
"style": {"label":"风格"},
|
||||||
|
"setup": {"label":"设置"},
|
||||||
|
"install": {"label":"安装"},
|
||||||
|
"remove": {"label":"移除"},
|
||||||
|
"update": {"label":"更新"},
|
||||||
|
"about": {"label":"关于"},
|
||||||
|
"system": {"label":"系统"},
|
||||||
|
"system.screensaver": {"label":"屏保"},
|
||||||
|
"system.lock": {"label":"锁屏"},
|
||||||
|
"system.suspend": {"label":"睡眠"},
|
||||||
|
"system.hibernate": {"label":"休眠"},
|
||||||
|
"system.logout": {"label":"注销"},
|
||||||
|
"system.reboot": {"label":"重启"},
|
||||||
|
"system.shutdown": {"label":"关机"},
|
||||||
|
"learn.keybindings": {"label":"快捷键"},
|
||||||
|
"learn.omarchy": {"label":"Omarchy 手册"},
|
||||||
|
"learn.community": {"label":"社区"},
|
||||||
|
"trigger.emoji": {"label":"表情"},
|
||||||
|
"trigger.reminder": {"label":"提醒"},
|
||||||
|
"trigger.capture": {"label":"截取"},
|
||||||
|
"trigger.capture.screenshot": {"label":"截图"},
|
||||||
|
"trigger.capture.screenrecord.stop": {"label":"停止录屏"},
|
||||||
|
"trigger.capture.screenrecord": {"label":"录屏"},
|
||||||
|
"trigger.capture.text": {"label":"OCR 取字"},
|
||||||
|
"trigger.capture.qr": {"label":"二维码"},
|
||||||
|
"trigger.capture.color": {"label":"取色"},
|
||||||
|
"trigger.capture.screenrecord.no-audio": {"label":"无音频"},
|
||||||
|
"trigger.capture.screenrecord.desktop-audio": {"label":"含桌面音频"},
|
||||||
|
"trigger.capture.screenrecord.microphone": {"label":"含桌面与麦克风音频"},
|
||||||
|
"trigger.capture.screenrecord.webcam": {"label":"含桌面、麦克风与摄像头"},
|
||||||
|
"trigger.transcode": {"label":"转码"},
|
||||||
|
"trigger.share": {"label":"分享"},
|
||||||
|
"trigger.toggle": {"label":"开关"},
|
||||||
|
"trigger.hardware": {"label":"硬件"},
|
||||||
|
"trigger.tests": {"label":"测速"},
|
||||||
|
"trigger.hardware.laptop-display": {"label":"笔记本屏幕"},
|
||||||
|
"trigger.hardware.mirror-display": {"label":"镜像显示"},
|
||||||
|
"trigger.hardware.hybrid-gpu": {"label":"混合显卡"},
|
||||||
|
"trigger.hardware.touchpad": {"label":"触控板"},
|
||||||
|
"trigger.hardware.touchpad-haptics": {"label":"触控板振动"},
|
||||||
|
"trigger.hardware.touchpad-haptics.low": {"label":"低"},
|
||||||
|
"trigger.hardware.touchpad-haptics.mid": {"label":"中"},
|
||||||
|
"trigger.hardware.touchpad-haptics.high": {"label":"高"},
|
||||||
|
"trigger.hardware.touchscreen": {"label":"触摸屏"},
|
||||||
|
"trigger.reminder.set": {"label":"设一个提醒"},
|
||||||
|
"trigger.reminder.show": {"label":"查看全部"},
|
||||||
|
"trigger.reminder.clear": {"label":"清除全部"},
|
||||||
|
"trigger.share.clipboard": {"label":"剪贴板"},
|
||||||
|
"trigger.share.file": {"label":"文件"},
|
||||||
|
"trigger.share.folder": {"label":"文件夹"},
|
||||||
|
"trigger.share.receive": {"label":"接收"},
|
||||||
|
"trigger.toggle.idle-lock": {"label":"保持唤醒"},
|
||||||
|
"trigger.toggle.notifications": {"label":"通知"},
|
||||||
|
"trigger.toggle.crash-capture": {"label":"崩溃捕获"},
|
||||||
|
"trigger.toggle.screensaver": {"label":"屏保"},
|
||||||
|
"trigger.toggle.nightlight": {"label":"夜间模式"},
|
||||||
|
"trigger.toggle.top-bar": {"label":"菜单栏"},
|
||||||
|
"trigger.toggle.battery-percentage": {"label":"电量百分比"},
|
||||||
|
"trigger.toggle.workspace-layout": {"label":"工作区布局"},
|
||||||
|
"trigger.toggle.window-gaps": {"label":"窗口间距"},
|
||||||
|
"trigger.toggle.one-window-ratio": {"label":"单窗口比例"},
|
||||||
|
"trigger.tests.network-speedtest": {"label":"网络测速"},
|
||||||
|
"trigger.tests.disk-speedtest": {"label":"磁盘测速"},
|
||||||
|
"style.theme": {"label":"主题"},
|
||||||
|
"style.background": {"label":"壁纸"},
|
||||||
|
"style.unlock": {"label":"解锁画面"},
|
||||||
|
"style.font": {"label":"字体"},
|
||||||
|
"style.bar": {"label":"菜单栏"},
|
||||||
|
"style.bar.position": {"label":"位置"},
|
||||||
|
"style.bar.transparency": {"label":"透明"},
|
||||||
|
"style.screensaver": {"label":"屏保"},
|
||||||
|
"style.about": {"label":"关于"},
|
||||||
|
"style.bar.position.top": {"label":"顶部"},
|
||||||
|
"style.bar.position.bottom": {"label":"底部"},
|
||||||
|
"style.bar.position.left": {"label":"左侧"},
|
||||||
|
"style.bar.position.right": {"label":"右侧"},
|
||||||
|
"style.about.text": {"label":"编辑文字"},
|
||||||
|
"style.about.image": {"label":"取自图片"},
|
||||||
|
"style.about.default": {"label":"恢复默认"},
|
||||||
|
"style.screensaver.text": {"label":"编辑文字"},
|
||||||
|
"style.screensaver.image": {"label":"取自图片"},
|
||||||
|
"style.screensaver.default": {"label":"恢复默认"},
|
||||||
|
"setup.monitors": {"label":"显示器"},
|
||||||
|
"setup.keybindings": {"label":"快捷键"},
|
||||||
|
"setup.input": {"label":"输入设备"},
|
||||||
|
"setup.network": {"label":"网络"},
|
||||||
|
"setup.network.dns.custom": {"label":"自定义"},
|
||||||
|
"setup.network.qr": {"label":"二维码"},
|
||||||
|
"setup.default": {"label":"默认程序"},
|
||||||
|
"setup.default.agent": {"title":"默认 Agent"},
|
||||||
|
"setup.default.browser": {"label":"浏览器","title":"默认浏览器"},
|
||||||
|
"setup.default.terminal": {"label":"终端","title":"默认终端"},
|
||||||
|
"setup.default.editor": {"label":"编辑器","title":"默认编辑器"},
|
||||||
|
"setup.region": {"label":"区域"},
|
||||||
|
"setup.region.world": {"label":"世界"},
|
||||||
|
"setup.region.china": {"label":"中国"},
|
||||||
|
"setup.plugin": {"label":"插件"},
|
||||||
|
"setup.plugin.enable": {"label":"启用插件"},
|
||||||
|
"setup.plugin.disable": {"label":"停用插件"},
|
||||||
|
"setup.plugin.add": {"label":"添加插件"},
|
||||||
|
"setup.plugin.clone": {"label":"克隆插件"},
|
||||||
|
"setup.plugin.remove": {"label":"移除插件"},
|
||||||
|
"setup.security": {"label":"安全"},
|
||||||
|
"setup.config": {"label":"配置"},
|
||||||
|
"setup.security.fingerprint": {"label":"指纹"},
|
||||||
|
"setup.security.passwordless-sudo": {"label":"免密 sudo"},
|
||||||
|
"setup.security.sudoless-docker": {"label":"免 sudo Docker"},
|
||||||
|
"setup.direct-boot": {"label":"直接启动"},
|
||||||
|
"setup.reset": {"label":"恢复出厂设置"},
|
||||||
|
"install.package": {"label":"软件包"},
|
||||||
|
"install.service": {"label":"服务"},
|
||||||
|
"install.development": {"label":"开发环境"},
|
||||||
|
"install.editor": {"label":"编辑器"},
|
||||||
|
"install.style": {"label":"风格"},
|
||||||
|
"install.style.theme": {"label":"主题"},
|
||||||
|
"install.style.background": {"label":"壁纸"},
|
||||||
|
"install.style.font": {"label":"字体"},
|
||||||
|
"install.gaming": {"label":"游戏"},
|
||||||
|
"install.browser": {"label":"浏览器"},
|
||||||
|
"install.webapp": {"label":"网页应用"},
|
||||||
|
"install.terminal": {"label":"终端"},
|
||||||
|
"install.preinstalls": {"label":"预装应用"},
|
||||||
|
"install.service.chromium-account": {"label":"Chromium 账号"},
|
||||||
|
"install.ai.dictation": {"label":"语音听写"},
|
||||||
|
"install.gaming.xbox-cloud": {"label":"Xbox 云游戏"},
|
||||||
|
"install.gaming.xbox-controllers": {"label":"Xbox 手柄"},
|
||||||
|
"install.gaming.retro-launcher": {"label":"RetroArch 启动器"},
|
||||||
|
"install.development.docker-dbs": {"label":"Docker 数据库"},
|
||||||
|
"remove.package": {"label":"软件包"},
|
||||||
|
"remove.ai": {"title":"移除"},
|
||||||
|
"remove.service": {"label":"服务","title":"移除"},
|
||||||
|
"remove.development": {"label":"开发环境","title":"移除"},
|
||||||
|
"remove.theme": {"label":"主题"},
|
||||||
|
"remove.gaming": {"label":"游戏","title":"移除"},
|
||||||
|
"remove.browser": {"label":"浏览器","title":"移除"},
|
||||||
|
"remove.webapp": {"label":"网页应用"},
|
||||||
|
"remove.preinstalls": {"label":"预装应用"},
|
||||||
|
"remove.security": {"label":"安全","title":"移除"},
|
||||||
|
"remove.security.fingerprint": {"label":"指纹"},
|
||||||
|
"remove.security.sudoless-docker": {"label":"免 sudo Docker"},
|
||||||
|
"remove.ai.dictation": {"label":"语音听写"},
|
||||||
|
"remove.gaming.xbox-cloud": {"label":"Xbox 云游戏"},
|
||||||
|
"remove.gaming.xbox-controllers": {"label":"Xbox 手柄 ()"},
|
||||||
|
"remove.development.javascript": {"title":"移除"},
|
||||||
|
"remove.development.php": {"title":"移除"},
|
||||||
|
"remove.development.elixir": {"title":"移除"},
|
||||||
|
"update.channel": {"label":"通道"},
|
||||||
|
"update.config": {"label":"配置","title":"重置为默认"},
|
||||||
|
"update.themes": {"label":"附加主题"},
|
||||||
|
"update.process": {"label":"进程","title":"重启"},
|
||||||
|
"update.hardware": {"label":"硬件","title":"重启"},
|
||||||
|
"update.firmware": {"label":"固件"},
|
||||||
|
"update.password": {"label":"密码"},
|
||||||
|
"update.timezone": {"label":"时区"},
|
||||||
|
"update.time": {"label":"时间"},
|
||||||
|
"update.hardware.audio": {"label":"音频"},
|
||||||
|
"update.hardware.bluetooth": {"label":"蓝牙"},
|
||||||
|
"update.hardware.trackpad": {"label":"触控板"},
|
||||||
|
"update.password.drive": {"label":"磁盘加密"},
|
||||||
|
"update.password.user": {"label":"用户"},
|
||||||
|
}
|
||||||
@@ -61,6 +61,8 @@ On Dell XPS laptops with a haptic touchpad, you can also set the click strength
|
|||||||
|
|
||||||
Omarchy runs the [fcitx5](https://fcitx-im.org/) input method framework as part of every session — it's what powers the CapsLock compose sequences. That means the plumbing for non-Latin input is already in place: install an input engine like `fcitx5-mozc` (Japanese) or `fcitx5-chinese-addons` (Chinese) with `omarchy pkg add`, plus `fcitx5-configtool` to add the engine to your input methods and set the key that switches between them.
|
Omarchy runs the [fcitx5](https://fcitx-im.org/) input method framework as part of every session — it's what powers the CapsLock compose sequences. That means the plumbing for non-Latin input is already in place: install an input engine like `fcitx5-mozc` (Japanese) or `fcitx5-chinese-addons` (Chinese) with `omarchy pkg add`, plus `fcitx5-configtool` to add the engine to your input methods and set the key that switches between them.
|
||||||
|
|
||||||
|
For Chinese there's a shortcut: flip _Setup > Region_ to China and Omarchy installs and configures the [Rime Ice](https://github.com/iDvel/rime-ice) input method for you, using [Andy Stewart's installer](https://github.com/manateelazycat/rime-ice-installer), and switches the system language to Chinese from the next login. Rime comes up active by default, `Ctrl+Space` toggles back to plain English input, and the Omarchy menu itself puts on Chinese labels.
|
||||||
|
|
||||||
### Use ALT as SUPER
|
### Use ALT as SUPER
|
||||||
|
|
||||||
On some keyboards, it's not convenient to use the primary meta key (Windows/cmd key) as SUPER. You can change this to be ALT instead using this change:
|
On some keyboards, it's not convenient to use the primary meta key (Windows/cmd key) as SUPER. You can change this to be ALT instead using this change:
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ function parseMenuJsonc(raw) {
|
|||||||
for (var id in source) {
|
for (var id in source) {
|
||||||
var entry = source[id]
|
var entry = source[id]
|
||||||
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue
|
||||||
out.push(normalizeItem(id, entry))
|
var item = normalizeItem(id, entry)
|
||||||
|
item.declared = Object.keys(entry)
|
||||||
|
out.push(item)
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
@@ -74,11 +76,25 @@ function mergeMenuSources(defaultItems, userItems) {
|
|||||||
var entry = src[i]
|
var entry = src[i]
|
||||||
if (!entry || !entry.id) continue
|
if (!entry || !entry.id) continue
|
||||||
if (!nextItems[entry.id]) nextOrder.push(entry.id)
|
if (!nextItems[entry.id]) nextOrder.push(entry.id)
|
||||||
var prior = nextItems[entry.id] || {}
|
var prior = nextItems[entry.id]
|
||||||
var merged = {}
|
var merged = {}
|
||||||
|
// Overriding an existing id only replaces the fields the file declared,
|
||||||
|
// so a label-only entry retitles a row without clearing its action.
|
||||||
|
if (prior && entry.declared) {
|
||||||
for (var k in prior) merged[k] = prior[k]
|
for (var k in prior) merged[k] = prior[k]
|
||||||
|
for (var d = 0; d < entry.declared.length; d++) {
|
||||||
|
var key = entry.declared[d]
|
||||||
|
if (key in entry) merged[key] = entry[key]
|
||||||
|
}
|
||||||
|
if (entry.declared.indexOf("action") >= 0 || entry.declared.indexOf("target") >= 0)
|
||||||
|
merged.kind = merged.action ? "action" : (merged.target ? "link" : "menu")
|
||||||
|
} else {
|
||||||
|
prior = prior || {}
|
||||||
|
for (var k1 in prior) merged[k1] = prior[k1]
|
||||||
for (var k2 in entry) merged[k2] = entry[k2]
|
for (var k2 in entry) merged[k2] = entry[k2]
|
||||||
|
}
|
||||||
merged.id = entry.id
|
merged.id = entry.id
|
||||||
|
delete merged.declared
|
||||||
nextItems[entry.id] = merged
|
nextItems[entry.id] = merged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -398,7 +414,8 @@ var GUARD_READERS = [
|
|||||||
"omarchy-default-browser",
|
"omarchy-default-browser",
|
||||||
"omarchy-default-editor",
|
"omarchy-default-editor",
|
||||||
"omarchy-default-terminal",
|
"omarchy-default-terminal",
|
||||||
"omarchy-dns"
|
"omarchy-dns",
|
||||||
|
"omarchy-region"
|
||||||
]
|
]
|
||||||
|
|
||||||
// Package and command presence account for most of what the guards ask, and
|
// Package and command presence account for most of what the guards ask, and
|
||||||
|
|||||||
@@ -44,9 +44,10 @@ assertDeepEqual(
|
|||||||
aliases: ['theme'],
|
aliases: ['theme'],
|
||||||
when: '',
|
when: '',
|
||||||
checked: '',
|
checked: '',
|
||||||
disabled: ''
|
disabled: '',
|
||||||
|
declared: ['label', 'aliases', 'description', 'action']
|
||||||
},
|
},
|
||||||
'menu normalizes parsed items'
|
'menu normalizes parsed items and records what the file declared'
|
||||||
)
|
)
|
||||||
|
|
||||||
const user = [
|
const user = [
|
||||||
@@ -58,6 +59,16 @@ assertEqual(merged.items['style.theme'].label, 'Theme picker', 'menu user entrie
|
|||||||
assertEqual(merged.items['style.theme'].order, 2, 'menu preserves original order on override')
|
assertEqual(merged.items['style.theme'].order, 2, 'menu preserves original order on override')
|
||||||
assert(merged.items.root, 'menu injects root when merging sources')
|
assert(merged.items.root, 'menu injects root when merging sources')
|
||||||
|
|
||||||
|
const retitled = menu.mergeMenuSources(
|
||||||
|
menu.parseMenuJsonc('{"apps": {"icon":"A","label":"Apps","provider":"apps"}, "apps.go": {"label":"Go","action":"run-go"}}'),
|
||||||
|
menu.parseMenuJsonc('{"apps": {"label":"应用"}, "apps.go": {"label":"出发"}}')
|
||||||
|
)
|
||||||
|
assertEqual(retitled.items['apps'].provider, 'apps', 'menu keeps the provider when a user entry only retitles')
|
||||||
|
assertEqual(retitled.items['apps'].icon, 'A', 'menu keeps the icon when a user entry only retitles')
|
||||||
|
assertEqual(retitled.items['apps.go'].action, 'run-go', 'menu keeps the action when a user entry only retitles')
|
||||||
|
assertEqual(retitled.items['apps.go'].kind, 'action', 'menu keeps the kind when a user entry only retitles')
|
||||||
|
assertEqual(retitled.items['apps.go'].label, '出发', 'menu applies the user retitle')
|
||||||
|
|
||||||
assertEqual(menu.slugify('Power Saver!'), 'power-saver', 'menu slugifies provider rows')
|
assertEqual(menu.slugify('Power Saver!'), 'power-saver', 'menu slugifies provider rows')
|
||||||
assertEqual(menu.pathFor(merged.items, 'style.theme'), 'Style › Theme picker', 'menu builds item paths')
|
assertEqual(menu.pathFor(merged.items, 'style.theme'), 'Style › Theme picker', 'menu builds item paths')
|
||||||
assertEqual(menu.parentPathFor(merged.items, 'style.theme'), 'Style', 'menu builds parent paths')
|
assertEqual(menu.parentPathFor(merged.items, 'style.theme'), 'Style', 'menu builds parent paths')
|
||||||
@@ -261,6 +272,22 @@ assert(
|
|||||||
'menu always exposes every supported browser, terminal, and editor under Defaults'
|
'menu always exposes every supported browser, terminal, and editor under Defaults'
|
||||||
)
|
)
|
||||||
assert(!defaultById['install.ai.crush'], 'menu removes Crush from Install > AI')
|
assert(!defaultById['install.ai.crush'], 'menu removes Crush from Install > AI')
|
||||||
|
const regionEntries = defaultItems.filter(item => item.parent === 'setup.region')
|
||||||
|
assert(
|
||||||
|
regionEntries.map(item => item.label).join('\0') === 'World\0China'
|
||||||
|
&& regionEntries.every(item => item.checked.includes(`== \"${item.label}\"`) && !item.when)
|
||||||
|
&& defaultById['setup.region.world'].action.includes('omarchy-region world')
|
||||||
|
&& defaultById['setup.region.china'].action.includes('omarchy-region china'),
|
||||||
|
'menu offers World and China under Setup > Region'
|
||||||
|
)
|
||||||
|
const zhOverlay = fs.readFileSync(path.join(root, 'default/omarchy/omarchy-menu.zh-cn.jsonc'), 'utf8')
|
||||||
|
const zhItems = menu.parseMenuJsonc(zhOverlay)
|
||||||
|
assert(zhItems.length > 100, 'Chinese menu overlay parses')
|
||||||
|
assert(zhItems.every(item => defaultById[item.id]), 'Chinese menu overlay only retitles shipped entries')
|
||||||
|
assert(
|
||||||
|
!/"(action|target|icon|when|checked|disabled|provider|aliases)"/.test(zhOverlay),
|
||||||
|
'Chinese menu overlay declares labels and titles only'
|
||||||
|
)
|
||||||
// Software you already have keeps its place in Install, dimmed rather than
|
// Software you already have keeps its place in Install, dimmed rather than
|
||||||
// dropped, so the list reads as a catalog of what Omarchy can install.
|
// dropped, so the list reads as a catalog of what Omarchy can install.
|
||||||
// Chromium Account is the sole Install row with anything left to hide for, so
|
// Chromium Account is the sole Install row with anything left to hide for, so
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
|
test_tmp=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$test_tmp"' EXIT
|
||||||
|
|
||||||
|
mock_bin="$test_tmp/bin"
|
||||||
|
test_home="$test_tmp/home"
|
||||||
|
pkg_log="$test_tmp/pkg-add"
|
||||||
|
installer_log="$test_tmp/installer"
|
||||||
|
sudo_log="$test_tmp/sudo"
|
||||||
|
systemctl_log="$test_tmp/systemctl"
|
||||||
|
mkdir -p "$mock_bin" "$test_home"
|
||||||
|
|
||||||
|
cat >"$mock_bin/omarchy-pkg-add" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
printf '%s\n' "$*" >>"$OMARCHY_TEST_PKG_LOG"
|
||||||
|
SH
|
||||||
|
|
||||||
|
cat >"$mock_bin/sudo" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
printf '%s\n' "$*" >>"$OMARCHY_TEST_SUDO_LOG"
|
||||||
|
SH
|
||||||
|
|
||||||
|
cat >"$mock_bin/systemctl" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
printf '%s\n' "$*" >>"$OMARCHY_TEST_SYSTEMCTL_LOG"
|
||||||
|
SH
|
||||||
|
|
||||||
|
cat >"$mock_bin/rime-ice-installer" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
printf 'ran\n' >>"$OMARCHY_TEST_INSTALLER_LOG"
|
||||||
|
mkdir -p "$HOME/.local/share/fcitx5/rime"
|
||||||
|
touch "$HOME/.local/share/fcitx5/rime/rime_ice.schema.yaml"
|
||||||
|
SH
|
||||||
|
|
||||||
|
chmod +x "$mock_bin"/*
|
||||||
|
|
||||||
|
export HOME="$test_home"
|
||||||
|
export PATH="$mock_bin:$ROOT/bin:$PATH"
|
||||||
|
export OMARCHY_TEST_PKG_LOG="$pkg_log"
|
||||||
|
export OMARCHY_TEST_INSTALLER_LOG="$installer_log"
|
||||||
|
export OMARCHY_TEST_SUDO_LOG="$sudo_log"
|
||||||
|
export OMARCHY_TEST_SYSTEMCTL_LOG="$systemctl_log"
|
||||||
|
export OMARCHY_PATH="$ROOT"
|
||||||
|
|
||||||
|
[[ $(omarchy-region) == "World" ]] || fail "region defaults to World before any choice"
|
||||||
|
pass "region defaults to World before any choice"
|
||||||
|
|
||||||
|
mkdir -p "$test_home/.config/omarchy/extensions"
|
||||||
|
cp "$ROOT/config/omarchy/extensions/omarchy-menu.jsonc" "$test_home/.config/omarchy/extensions/omarchy-menu.jsonc"
|
||||||
|
|
||||||
|
omarchy-region china >/dev/null
|
||||||
|
cmp -s "$ROOT/default/omarchy/omarchy-menu.zh-cn.jsonc" "$test_home/.config/omarchy/extensions/omarchy-menu.jsonc" ||
|
||||||
|
fail "china installs the Chinese menu labels over the stock sample"
|
||||||
|
grep -q "locale-gen" "$sudo_log" || fail "china generates the Chinese locale"
|
||||||
|
grep -Fx "localectl set-locale LANG=zh_CN.UTF-8" "$sudo_log" >/dev/null || fail "china sets the system language"
|
||||||
|
grep -Fx "pacman -Sy" "$sudo_log" >/dev/null || fail "china syncs the repo databases before installing"
|
||||||
|
grep -Fx "rime-ice-installer" "$pkg_log" >/dev/null || fail "china installs the Rime Ice installer package"
|
||||||
|
(( $(wc -l <"$installer_log") == 1 )) || fail "china runs the Rime Ice installer"
|
||||||
|
grep -A1 '^\[Groups/0/Items/0\]$' "$test_home/.config/fcitx5/profile" | grep -qFx "Name=rime" ||
|
||||||
|
fail "china makes Rime the first input method"
|
||||||
|
grep -qFx "DefaultIM=rime" "$test_home/.config/fcitx5/profile" || fail "china makes Rime the group default"
|
||||||
|
grep -qFx "0=Control+space" "$test_home/.config/fcitx5/config" || fail "china pins the Ctrl+Space toggle"
|
||||||
|
grep -Fx -- "--user restart omarchy-fcitx5.service" "$systemctl_log" >/dev/null ||
|
||||||
|
fail "china restarts fcitx5 with the new profile"
|
||||||
|
[[ $(omarchy-region) == "China" ]] || fail "china is stored as the region"
|
||||||
|
pass "china sets up the language, menu and input method"
|
||||||
|
|
||||||
|
omarchy-region china >/dev/null
|
||||||
|
(( $(wc -l <"$installer_log") == 1 )) || fail "reapplying china skips the installed input method"
|
||||||
|
(( $(grep -cFx "pacman -Sy" "$sudo_log") == 1 )) || fail "reapplying china skips the database sync too"
|
||||||
|
pass "reapplying china is idempotent and leaves the input method alone"
|
||||||
|
|
||||||
|
omarchy-region world >/dev/null
|
||||||
|
cmp -s "$ROOT/config/omarchy/extensions/omarchy-menu.jsonc" "$test_home/.config/omarchy/extensions/omarchy-menu.jsonc" ||
|
||||||
|
fail "world restores the sample menu extension"
|
||||||
|
[[ $(omarchy-region) == "World" ]] || fail "world is stored as the region"
|
||||||
|
[[ -f $test_home/.local/share/fcitx5/rime/rime_ice.schema.yaml ]] || fail "world leaves the input method installed"
|
||||||
|
grep -qFx "DefaultIM=rime" "$test_home/.config/fcitx5/profile" || fail "world leaves the input method configured"
|
||||||
|
pass "world restores the menu and keeps the input method"
|
||||||
|
|
||||||
|
printf '{"apps": {"label":"Mine"}}\n' >"$test_home/.config/omarchy/extensions/omarchy-menu.jsonc"
|
||||||
|
if omarchy-region china >/dev/null 2>"$test_tmp/refusal"; then
|
||||||
|
fail "china refuses a menu extension someone wrote by hand"
|
||||||
|
fi
|
||||||
|
grep -q "menu:" "$test_tmp/refusal" || fail "china names the menu extension it refuses to touch"
|
||||||
|
[[ $(omarchy-region) == "World" ]] || fail "a refused china run keeps the region"
|
||||||
|
pass "china refuses a hand-written menu extension"
|
||||||
|
grep -Fx '{"apps": {"label":"Mine"}}' "$test_home/.config/omarchy/extensions/omarchy-menu.jsonc" >/dev/null ||
|
||||||
|
fail "a refused china run leaves the extension untouched"
|
||||||
|
|
||||||
|
omarchy-region world >/dev/null
|
||||||
|
grep -Fx '{"apps": {"label":"Mine"}}' "$test_home/.config/omarchy/extensions/omarchy-menu.jsonc" >/dev/null ||
|
||||||
|
fail "world leaves a menu extension someone wrote by hand"
|
||||||
|
pass "world only restores the extension Omarchy wrote"
|
||||||
|
|
||||||
|
if omarchy-region china unexpected >/dev/null 2>&1; then
|
||||||
|
fail "region rejects extra arguments"
|
||||||
|
fi
|
||||||
|
[[ $(omarchy-region) == "World" ]] || fail "extra arguments change nothing"
|
||||||
|
pass "region rejects extra arguments"
|
||||||
Reference in New Issue
Block a user