Compare commits
26
Commits
e5b01ace6b
...
3c6e7cca55
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c6e7cca55 | ||
|
|
02fb6439eb | ||
|
|
c8ec83d046 | ||
|
|
8e1f9266ca | ||
|
|
fcd681c099 | ||
|
|
76b9331d8b | ||
|
|
c08a1bc3c2 | ||
|
|
2d2aa337e4 | ||
|
|
738d59e3a9 | ||
|
|
7198a7b711 | ||
|
|
c352b62d67 | ||
|
|
15b3529ef2 | ||
|
|
057681b33c | ||
|
|
50d687a1f2 | ||
|
|
02d28d273a | ||
|
|
5ad44d62f6 | ||
|
|
c24889096e | ||
|
|
1554a522cc | ||
|
|
717a0e9c50 | ||
|
|
34016cfb27 | ||
|
|
b83d3df084 | ||
|
|
902fd8aebd | ||
|
|
a137760c89 | ||
|
|
37207f1911 | ||
|
|
060c57b4aa | ||
|
|
0a55a3c554 |
No files matched your search
@@ -49,3 +49,7 @@ unicode characters. Agent file-editing tools can strip multi-byte codepoints
|
||||
in some positions — do **not** rewrite widget files wholesale through those
|
||||
tools. For glyph fixes, make a targeted edit with the surrounding context, or
|
||||
use a Python script that inserts codepoints via `chr(0xXXXXX)`.
|
||||
|
||||
## Palette references
|
||||
|
||||
Import `qs.Commons as Commons` and use `Commons.Color` for the palette singleton, including `Connections.target`. Qt 6.12 introduces a `Color` type in `QtQuick` that shadows unqualified palette references. This applies to first-party components and third-party plugin QML alike. Keep the unqualified `qs.Commons` import when the file also uses `Style`, `Util`, or `Border`.
|
||||
@@ -46,6 +46,7 @@ GROUP_DESCRIPTIONS[finalize]="Finalize user setup"
|
||||
GROUP_DESCRIPTIONS[default]="Default application selection"
|
||||
GROUP_DESCRIPTIONS[dev]="Omarchy development tools"
|
||||
GROUP_DESCRIPTIONS[disk]="Disk performance helpers"
|
||||
GROUP_DESCRIPTIONS[dictation]="Dictation recording"
|
||||
GROUP_DESCRIPTIONS[display]="Display and text scaling"
|
||||
GROUP_DESCRIPTIONS[dns]="DNS resolver configuration"
|
||||
GROUP_DESCRIPTIONS[drive]="Drive selection and encryption"
|
||||
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Get or set the default dictation backend
|
||||
# omarchy:args=[backend]
|
||||
# omarchy:examples=omarchy default dictation | omarchy default dictation superwhisper
|
||||
|
||||
if (($# > 1)); then
|
||||
echo "Usage: omarchy default dictation [backend]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (($# == 0)); then
|
||||
config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
|
||||
if [[ ! -s $config ]]; then
|
||||
echo "No dictation backend configured. Select one in Setup > Defaults > Dictation." >&2
|
||||
exit 1
|
||||
fi
|
||||
read -r backend < "$config"
|
||||
else
|
||||
backend=$1
|
||||
fi
|
||||
|
||||
if [[ ! $backend =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
|
||||
echo "Invalid dictation backend: $backend" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (($# == 0)); then
|
||||
echo "$backend"
|
||||
else
|
||||
installer="omarchy-install-dictation-$backend"
|
||||
if omarchy-cmd-missing "$installer"; then
|
||||
echo "No installer available for dictation backend: $backend" >&2
|
||||
exit 1
|
||||
fi
|
||||
omarchy-launch-floating-terminal-with-presentation "$installer"
|
||||
fi
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Record dictation with the configured backend
|
||||
# omarchy:args=<start|stop|toggle>
|
||||
# omarchy:examples=omarchy dictation start | omarchy dictation toggle
|
||||
|
||||
if (($# != 1)) || [[ $1 != "start" && $1 != "stop" && $1 != "toggle" ]]; then
|
||||
echo "Usage: omarchy dictation <start|stop|toggle>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
|
||||
mkdir -p "${config%/*}"
|
||||
exec 9> "$config.lock"
|
||||
if ! flock -w 5 9; then
|
||||
echo "Dictation is busy; try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
backend=$(omarchy-default-dictation) || exit 1
|
||||
adapter="omarchy-dictation-$backend"
|
||||
if omarchy-cmd-missing "$adapter"; then
|
||||
echo "The selected dictation backend ($backend) is not installed." >&2
|
||||
exit 1
|
||||
fi
|
||||
"$adapter" "$1" 9>&-
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Record dictation using Superwhisper
|
||||
# omarchy:args=<start|stop|toggle>
|
||||
|
||||
case "${1:-}" in
|
||||
start) superwhisper start ;;
|
||||
stop)
|
||||
if omarchy-cmd-missing superwhisper || ! systemctl --user is-active --quiet superwhisper.service; then
|
||||
exit 0
|
||||
else
|
||||
superwhisper stop
|
||||
fi
|
||||
;;
|
||||
toggle) superwhisper record ;;
|
||||
*) echo "Usage: omarchy dictation superwhisper <start|stop|toggle>" >&2; exit 1 ;;
|
||||
esac
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:hidden=true
|
||||
# omarchy:summary=Save a configured dictation backend after stopping the previous backend
|
||||
# omarchy:args=<backend>
|
||||
|
||||
set -e
|
||||
|
||||
if (($# != 1)) || [[ ! $1 =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
|
||||
echo "Usage: omarchy-dictation-use <backend>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
|
||||
mkdir -p "${config%/*}"
|
||||
exec 9> "$config.lock"
|
||||
if ! flock -w 5 9; then
|
||||
echo "Dictation is busy; try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
previous=$(omarchy-default-dictation 2>/dev/null) || previous=""
|
||||
if [[ -n $previous && $previous != "$1" ]] && omarchy-cmd-present "omarchy-dictation-$previous"; then
|
||||
if ! "omarchy-dictation-$previous" stop 9>&-; then
|
||||
echo "Could not stop $previous; keeping the existing dictation backend." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
temporary=$(mktemp "$config.XXXXXX")
|
||||
trap 'rm -f "$temporary"' EXIT
|
||||
printf '%s\n' "$1" > "$temporary"
|
||||
mv -f "$temporary" "$config"
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Record dictation using Voxtype
|
||||
# omarchy:args=<start|stop|toggle>
|
||||
|
||||
case "${1:-}" in
|
||||
start|toggle) voxtype record "$1" ;;
|
||||
stop)
|
||||
if omarchy-cmd-missing voxtype || [[ $(voxtype status) == "stopped" ]]; then
|
||||
exit 0
|
||||
else
|
||||
voxtype record stop
|
||||
fi
|
||||
;;
|
||||
*) echo "Usage: omarchy dictation voxtype <start|stop|toggle>" >&2; exit 1 ;;
|
||||
esac
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install and configure Superwhisper dictation
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
omarchy-pkg-add superwhisper-bin
|
||||
bash /usr/share/superwhisper/setup-user
|
||||
# Existing profiles may still use Right Alt, which conflicts with Omarchy.
|
||||
# Keep a recording shortcut active while moving away from that binding.
|
||||
shortcuts=$(superwhisper shortcuts show)
|
||||
if [[ $(jq -r '.shortcuts.hold' <<< "$shortcuts") == "RightAlt" ]]; then
|
||||
if [[ -z $(jq -r '.shortcuts.toggle' <<< "$shortcuts") ]]; then
|
||||
superwhisper shortcuts set hold "RightSuper" >/dev/null
|
||||
else
|
||||
superwhisper shortcuts set hold none >/dev/null
|
||||
fi
|
||||
fi
|
||||
# Superwhisper requires a native recording shortcut; keep it separate from
|
||||
# Omarchy's push-to-talk keys.
|
||||
superwhisper shortcuts set toggle "Alt+Space" >/dev/null
|
||||
superwhisper shortcuts set hold none >/dev/null
|
||||
omarchy-dictation-use superwhisper
|
||||
hyprctl reload >/dev/null
|
||||
omarchy-notification-send -g "$(omarchy-text "Superwhisper Dictation Ready")" "$(omarchy-text "Hold Right Alt or F9 to dictate (or toggle with Super + Ctrl + X).")" -t 10000
|
||||
superwhisper settings
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install and configure Voxtype dictation
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
if omarchy-cmd-missing voxtype; then
|
||||
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
|
||||
omarchy-pkg-add wtype voxtype-bin
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
mkdir -p "$config_home/voxtype"
|
||||
if [[ ! -e $config_home/voxtype/config.toml ]]; then
|
||||
cp "$OMARCHY_PATH/default/voxtype/config.toml" "$config_home/voxtype/"
|
||||
if omarchy-hw-vulkan; then
|
||||
voxtype setup gpu --enable || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Setup reuses the configured model and downloads it only when missing.
|
||||
# Retry these steps even when an earlier attempt already installed the binary.
|
||||
voxtype setup --download --no-post-install
|
||||
voxtype setup systemd
|
||||
systemctl --user enable --now voxtype.service
|
||||
|
||||
omarchy-dictation-use voxtype
|
||||
hyprctl reload >/dev/null
|
||||
omarchy-notification-send -g "$(omarchy-text "Voxtype Dictation Ready")" "$(omarchy-text "Hold Right Alt or F9 to dictate (or toggle with Super + Ctrl + X).")" -t 10000
|
||||
@@ -15,7 +15,9 @@
|
||||
# against a half-written tree, and that failed reload leaves a second engine
|
||||
# generation behind that turns the next restart's IPC kill into a crash.
|
||||
run_shell() {
|
||||
QS_DISABLE_FILE_WATCHER=1 QS_NO_RELOAD_POPUP=1 \
|
||||
local startup_background
|
||||
startup_background=$(readlink -f "$HOME/.local/state/omarchy/current/background" 2>/dev/null)
|
||||
OMARCHY_STARTUP_BACKGROUND="$startup_background" QS_DISABLE_FILE_WATCHER=1 QS_NO_RELOAD_POPUP=1 \
|
||||
systemd-cat -t omarchy-shell -- quickshell -n -p "$OMARCHY_PATH/shell" &
|
||||
shell_pid=$!
|
||||
|
||||
|
||||
@@ -68,8 +68,6 @@ run_first_run_step() {
|
||||
fi
|
||||
}
|
||||
|
||||
run_first_run_step "install Voxtype post-update hook" \
|
||||
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/install-voxtype.hook"
|
||||
run_first_run_step "install fingerprint setup post-update hook" \
|
||||
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/setup-fingerprint.hook"
|
||||
run_first_run_step "install agent setup post-update hook" \
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Remove Superwhisper dictation and its desktop integration
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
data_home="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
|
||||
systemctl --user disable --now superwhisper.service 2>/dev/null || true
|
||||
omarchy-shell -q shell setPluginEnabled superwhisper.panel false >/dev/null
|
||||
omarchy-pkg-drop superwhisper-bin
|
||||
systemctl --user daemon-reload
|
||||
|
||||
# Remove the package setup's links, preserving independently installed copies
|
||||
# and the user's preferences, history, and models.
|
||||
unlink_package_link() {
|
||||
if [[ -L $1 && $(readlink "$1") == "$2" ]]; then
|
||||
rm "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
unlink_package_link "$HOME/.local/bin/superwhisper" /usr/bin/superwhisper
|
||||
unlink_package_link "$data_home/superwhisper/app/current" /opt/superwhisper
|
||||
unlink_package_link "$config_home/omarchy/plugins/superwhisper-panel" /opt/superwhisper/assets/omarchy-plugin/superwhisper-panel
|
||||
for agent in .agents .claude .codex .pi/agent; do
|
||||
unlink_package_link "$HOME/$agent/skills/superwhisper" /opt/superwhisper/assets/agent-skill/superwhisper
|
||||
done
|
||||
|
||||
backend_config="$config_home/omarchy/defaults/dictation"
|
||||
if [[ -f $backend_config && $(< "$backend_config") == "superwhisper" ]]; then
|
||||
rm "$backend_config"
|
||||
fi
|
||||
|
||||
omarchy-shell -q shell rescanPlugins
|
||||
hyprctl reload >/dev/null
|
||||
if systemctl --user -q is-active omarchy-fcitx5.service; then
|
||||
systemctl --user restart omarchy-fcitx5.service
|
||||
fi
|
||||
@@ -18,6 +18,10 @@ if omarchy-cmd-present voxtype; then
|
||||
omarchy-pkg-drop voxtype-bin
|
||||
rm -rf ~/.config/voxtype
|
||||
rm -rf ~/.local/share/voxtype
|
||||
backend_config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
|
||||
if [[ -f $backend_config && $(< "$backend_config") == "voxtype" ]]; then
|
||||
rm "$backend_config"
|
||||
fi
|
||||
hyprctl reload >/dev/null
|
||||
else
|
||||
echo "Voxtype was not installed."
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Play the current background's matching theme intro
|
||||
# omarchy:hidden=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
state_home="$HOME/.local/state"
|
||||
current_dir="$state_home/omarchy/current"
|
||||
current_background_link="$current_dir/background"
|
||||
marker="$state_home/omarchy/background-intro.session-id"
|
||||
toggle="$state_home/omarchy/toggles/background-intros-off"
|
||||
video_extensions=(mp4 m4v mov webm mkv avi)
|
||||
|
||||
resolve_intro() {
|
||||
local background
|
||||
local stem
|
||||
local extension
|
||||
local intro
|
||||
|
||||
background=$(readlink -f "${1:-$current_background_link}" 2>/dev/null) || return 1
|
||||
[[ -f $background ]] || return 1
|
||||
case ${background,,} in
|
||||
*.jpg | *.jpeg | *.png | *.bmp | *.webp) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
|
||||
stem=${background##*/}
|
||||
stem=${stem%.*}
|
||||
for extension in "${video_extensions[@]}"; do
|
||||
intro="${background%/*}/intros/$stem.$extension"
|
||||
if [[ -f $intro ]]; then
|
||||
printf '%s\n' "$intro"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Preparation is speculative: the selected staged clip is compared again by OWE.
|
||||
prepare_theme_intro() {
|
||||
local theme="$1" background remembered name source intro
|
||||
local -a names=()
|
||||
[[ -n $theme && $theme != .* && $theme != */* ]] || return 0
|
||||
[[ ! -f $toggle ]] && ! animations_off || return 0
|
||||
owe warm >/dev/null 2>&1 || return 0
|
||||
[[ $(cat "$current_dir/theme.name" 2>/dev/null) != "$theme" ]] || return 0
|
||||
remembered=$(cat "$state_home/omarchy/theme-backgrounds/$theme" 2>/dev/null || true)
|
||||
if [[ ${remembered%/*} == "$current_dir/theme/backgrounds" ]]; then
|
||||
name=${remembered##*/}
|
||||
elif [[ -f $remembered ]]; then
|
||||
background="$remembered"
|
||||
else
|
||||
mapfile -d '' -t names < <(
|
||||
find -L "$HOME/.config/omarchy/backgrounds/$theme" -maxdepth 1 -type f -print0 2>/dev/null | sort -z
|
||||
)
|
||||
if (( ${#names[@]} > 0 )); then
|
||||
background=${names[0]}
|
||||
else
|
||||
mapfile -d '' -t names < <(
|
||||
find -L "$OMARCHY_PATH/themes/$theme/backgrounds" "$HOME/.config/omarchy/themes/$theme/backgrounds" -maxdepth 1 -type f \
|
||||
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.bmp' -o -iname '*.webp' \) -printf '%f\0' 2>/dev/null | sort -zu
|
||||
)
|
||||
(( ${#names[@]} > 0 )) || return 0
|
||||
name=${names[0]}
|
||||
fi
|
||||
fi
|
||||
if [[ -n ${name:-} ]]; then
|
||||
for source in "$HOME/.config/omarchy/themes/$theme" "$OMARCHY_PATH/themes/$theme"; do
|
||||
if [[ -f $source/backgrounds/$name ]]; then
|
||||
background="$source/backgrounds/$name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
[[ -n ${background:-} ]] || return 0
|
||||
intro=$(resolve_intro "$background" || true)
|
||||
[[ -z $intro ]] || owe intro-prepare "$intro" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
animations_off() {
|
||||
[[ $(hyprctl -j getoption animations:enabled 2>/dev/null | jq -r '.bool' 2>/dev/null) == "false" ]]
|
||||
}
|
||||
|
||||
theme_switch=false
|
||||
intro_prepared=false
|
||||
expected_background=""
|
||||
expected_theme=""
|
||||
case ${1:-} in
|
||||
"") ;;
|
||||
--prepare-theme)
|
||||
prepare_theme_intro "${2:-}"
|
||||
exit 0
|
||||
;;
|
||||
--resolve-only)
|
||||
resolve_intro "${2:-}" || true
|
||||
exit 0
|
||||
;;
|
||||
--resolve-startup)
|
||||
if [[ ! -f $toggle ]] && ! animations_off; then
|
||||
resolve_intro || true
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
--theme-switch)
|
||||
theme_switch=true
|
||||
expected_background=$(readlink -f "${2:-}" 2>/dev/null) || exit 1
|
||||
expected_theme=${3:-}
|
||||
intro_prepared=${4:-false}
|
||||
[[ -n $expected_theme ]] || exit 1
|
||||
;;
|
||||
*)
|
||||
echo "Usage: omarchy-theme-bg-boot-intro [--resolve-only [background]|--resolve-startup|--prepare-theme theme|--theme-switch background theme-id [prepared]]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $theme_switch == false ]]; then
|
||||
mkdir -p "$(dirname "$marker")"
|
||||
exec 9>"$state_home/omarchy/background-intro.lock"
|
||||
flock 9
|
||||
|
||||
session_id=${OMARCHY_SESSION_ID:-${HYPRLAND_INSTANCE_SIGNATURE:-}}
|
||||
[[ -n $session_id ]] || exit 0
|
||||
if [[ -f $marker && $(<"$marker") == "$session_id" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
marker_staged=$(mktemp "$state_home/omarchy/.background-intro.session-id.XXXXXX")
|
||||
trap 'rm -f "$marker_staged"' EXIT
|
||||
printf '%s\n' "$session_id" >"$marker_staged"
|
||||
mv -f "$marker_staged" "$marker"
|
||||
trap - EXIT
|
||||
exec 9>&-
|
||||
fi
|
||||
|
||||
selection_current() {
|
||||
[[ $(readlink -f "$current_background_link") == "$expected_background" &&
|
||||
$(stat -Lc '%d:%i' "$current_dir/theme" 2>/dev/null) == "$expected_theme" ]]
|
||||
}
|
||||
|
||||
if [[ $theme_switch == true ]] && ! selection_current; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -f $toggle ]] || animations_off; then
|
||||
[[ $theme_switch == false ]] || exit 1
|
||||
exit 0
|
||||
fi
|
||||
|
||||
intro=$(resolve_intro || true)
|
||||
if [[ -z $intro ]]; then
|
||||
[[ $theme_switch == false ]] || exit 1
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $theme_switch == true && $intro_prepared != "true" ]]; then
|
||||
owe refresh >/dev/null || exit 1
|
||||
elif [[ $theme_switch == false ]]; then
|
||||
deadline=$((SECONDS + 5))
|
||||
while ! timeout 1 owe status >/dev/null 2>&1; do
|
||||
if (( SECONDS >= deadline )); then
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
(( SECONDS < deadline )) || exit 0
|
||||
fi
|
||||
|
||||
# The asynchronous outgoing cover must reach the compositor before playback
|
||||
# can hide the shell background. Older shells have no readiness method.
|
||||
if [[ $theme_switch == true ]]; then
|
||||
deadline=$((SECONDS + 3))
|
||||
while true; do
|
||||
cover_status=$(omarchy-shell shell themeIntroCoverStatus "$expected_theme" 2>/dev/null || true)
|
||||
case $cover_status in
|
||||
loading)
|
||||
(( SECONDS < deadline )) || exit 1
|
||||
sleep 0.01
|
||||
;;
|
||||
error) exit 1 ;;
|
||||
superseded) exit 0 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ $theme_switch == true ]] && ! selection_current; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# A selection or setting changed while OWE was starting no longer permits this intro.
|
||||
if [[ ! -f $toggle && $intro == "$(resolve_intro || true)" ]] && ! animations_off; then
|
||||
if [[ $theme_switch == true && $intro_prepared == "true" ]]; then
|
||||
owe intro --start first-frame --refresh "$intro"
|
||||
else
|
||||
owe intro --start first-frame "$intro"
|
||||
fi
|
||||
elif [[ $theme_switch == true ]]; then
|
||||
exit 1
|
||||
fi
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Enable or disable background intros
|
||||
# omarchy:args=<enable|disable|toggle|status>
|
||||
# omarchy:examples=omarchy theme bg intro toggle | omarchy theme bg intro disable
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
toggle="$HOME/.local/state/omarchy/toggles/background-intros-off"
|
||||
case ${1:-} in
|
||||
enable)
|
||||
rm -f "$toggle"
|
||||
omarchy-notification-send "$(omarchy-text "Background Intros")" "$(omarchy-text "Enabled")"
|
||||
;;
|
||||
disable)
|
||||
mkdir -p "$(dirname "$toggle")"
|
||||
touch "$toggle"
|
||||
omarchy-notification-send "$(omarchy-text "Background Intros")" "$(omarchy-text "Disabled")"
|
||||
;;
|
||||
toggle)
|
||||
if [[ -f $toggle ]]; then
|
||||
omarchy-theme-bg-intro enable
|
||||
else
|
||||
omarchy-theme-bg-intro disable
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
[[ ! -f $toggle ]]
|
||||
;;
|
||||
*)
|
||||
echo "Usage: omarchy-theme-bg-intro <enable|disable|toggle|status>" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
+84
-6
@@ -50,6 +50,13 @@ shell_ipc() {
|
||||
timeout 2 omarchy-shell "$@" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
wait_for_theme_intro() {
|
||||
local method="$1" token="$2" pending="$3" deadline=$((SECONDS + 3))
|
||||
while (( SECONDS < deadline )) && [[ $(omarchy-shell shell "$method" "$token" 2>/dev/null) == "$pending" ]]; do
|
||||
sleep 0.02
|
||||
done
|
||||
}
|
||||
|
||||
is_video_path() {
|
||||
[[ ${1,,} =~ \.(mp4|m4v|mov|webm|mkv|avi)$ ]]
|
||||
}
|
||||
@@ -70,10 +77,18 @@ snapshot_background_path() {
|
||||
}
|
||||
|
||||
snapshot_current_background() {
|
||||
local current_background
|
||||
local current_background snapshot
|
||||
|
||||
current_background=$(readlink -f "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
|
||||
snapshot_background_path "$current_background" "previous"
|
||||
# An interrupted intro is still moving; its final still would flash as a
|
||||
# cover. Capture the frame already playing instead, when OWE supports it.
|
||||
mkdir -p "$BACKGROUND_TRANSITION_CACHE"
|
||||
snapshot="$BACKGROUND_TRANSITION_CACHE/previous-$$.ppm"
|
||||
if owe render "$(jq -nc --arg path "$snapshot" '{cmd: "snapshot", path: $path}')" >/dev/null 2>&1; then
|
||||
echo "$snapshot"
|
||||
else
|
||||
current_background=$(readlink -f "$CURRENT_BACKGROUND_LINK" 2>/dev/null || true)
|
||||
snapshot_background_path "$current_background" "previous"
|
||||
fi
|
||||
}
|
||||
|
||||
theme_background_state_file() {
|
||||
@@ -171,7 +186,7 @@ set_theme_background_link() {
|
||||
}
|
||||
|
||||
set_theme_background() {
|
||||
local new_background new_background_snapshot
|
||||
local new_background new_background_snapshot theme_id intro_prepared=false
|
||||
|
||||
if [[ -z $CHOSEN_THEME_BACKGROUND || ! -f $CHOSEN_THEME_BACKGROUND ]]; then
|
||||
if ! choose_theme_background; then
|
||||
@@ -182,6 +197,37 @@ set_theme_background() {
|
||||
fi
|
||||
|
||||
new_background="$CHOSEN_THEME_BACKGROUND"
|
||||
if [[ -n $THEME_BACKGROUND_INTRO ]]; then
|
||||
if [[ -n $INTRO_PREPARE_PID ]] && wait "$INTRO_PREPARE_PID"; then
|
||||
intro_prepared=true
|
||||
fi
|
||||
theme_id=$(stat -Lc '%d:%i' "$CURRENT_THEME_PATH")
|
||||
THEME_INTRO_TOKEN="$theme_id"
|
||||
shell_ipc shell prepareThemeIntro "$OLD_BACKGROUND_SNAPSHOT" "$theme_id" "$colors_payload" "$shell_payload" || \
|
||||
shell_ipc shell applyTheme "$colors_payload" "$shell_payload" || true
|
||||
ln -nsf "$new_background" "$CURRENT_BACKGROUND_LINK"
|
||||
(
|
||||
if ! omarchy-theme-bg-boot-intro --theme-switch "$new_background" "$theme_id" "$intro_prepared"; then
|
||||
if [[ $(readlink -f "$CURRENT_BACKGROUND_LINK") == "$(readlink -f "$new_background")" &&
|
||||
$(stat -Lc '%d:%i' "$CURRENT_THEME_PATH" 2>/dev/null) == "$theme_id" ]]; then
|
||||
shell_ipc background setInstant "$new_background" || true
|
||||
fi
|
||||
fi
|
||||
shell_ipc shell finishThemeIntro "$theme_id" || true
|
||||
wait_for_theme_intro themeIntroStatus "$theme_id" "pending"
|
||||
# Reloading Hyprland interrupts video presentation. Wait for playback,
|
||||
# then serialize with theme selection so an old intro cannot reload
|
||||
# the compositor during the next theme's video.
|
||||
(
|
||||
flock 9
|
||||
if [[ $(stat -Lc '%d:%i' "$CURRENT_THEME_PATH" 2>/dev/null) == "$theme_id" ]]; then
|
||||
omarchy-restart-hyprctl
|
||||
fi
|
||||
) 9>"$THEME_SET_LOCK"
|
||||
[[ -z $OLD_BACKGROUND_SNAPSHOT ]] || rm -f "$OLD_BACKGROUND_SNAPSHOT"
|
||||
) 9>&- &
|
||||
return
|
||||
fi
|
||||
new_background_snapshot=""
|
||||
if [[ -n $PREPARED_BACKGROUND_SNAPSHOT && $PREPARED_BACKGROUND == "$new_background" ]]; then
|
||||
new_background_snapshot="$PREPARED_BACKGROUND_SNAPSHOT"
|
||||
@@ -337,6 +383,12 @@ fi
|
||||
exec 9>"$THEME_SET_LOCK"
|
||||
flock 9
|
||||
|
||||
# A current video frame only represents the screen after its opening fade.
|
||||
# Finish that short reveal before capturing the next outgoing cover.
|
||||
if [[ $THEME_HEADLESS != "1" && $OMARCHY_THEME_SKIP_BACKGROUND != "1" ]]; then
|
||||
wait_for_theme_intro themeIntroStatus "$(stat -Lc '%d:%i' "$CURRENT_THEME_PATH" 2>/dev/null)" "pending"
|
||||
fi
|
||||
|
||||
PREVIOUS_THEME_NAME=$(cat "$CURRENT_THEME_NAME_PATH" 2>/dev/null || true)
|
||||
remember_current_theme_background
|
||||
|
||||
@@ -365,11 +417,24 @@ OLD_BACKGROUND_SNAPSHOT=""
|
||||
PREPARED_BACKGROUND=""
|
||||
PREPARED_BACKGROUND_SNAPSHOT=""
|
||||
BACKGROUND_TRANSITION_SNAPSHOTS=true
|
||||
THEME_BACKGROUND_INTRO=""
|
||||
INTRO_PREPARE_PID=""
|
||||
THEME_INTRO_TOKEN=""
|
||||
if [[ $THEME_HEADLESS != "1" && $OMARCHY_THEME_SKIP_BACKGROUND != "1" ]]; then
|
||||
# Resolve the staged choice while the old theme still exists. Video changes
|
||||
# switch directly to the durable path in QML, so neither side needs a copy.
|
||||
if choose_staged_theme_background; then
|
||||
if background_transition_uses_snapshots "$CHOSEN_THEME_BACKGROUND"; then
|
||||
if [[ $PREVIOUS_THEME_NAME != "$THEME_NAME" && ! -f $HOME/.local/state/omarchy/toggles/background-intros-off ]] &&
|
||||
[[ $(hyprctl -j getoption animations:enabled 2>/dev/null | jq -r '.bool') == "true" ]]; then
|
||||
THEME_BACKGROUND_INTRO=$(omarchy-theme-bg-boot-intro --resolve-only "$STAGED_THEME_BACKGROUND")
|
||||
fi
|
||||
if [[ -n $THEME_BACKGROUND_INTRO ]]; then
|
||||
OLD_BACKGROUND_SNAPSHOT=$(snapshot_current_background)
|
||||
# OWE retains the opened clip across the staged directory's rename.
|
||||
owe intro-prepare "$THEME_BACKGROUND_INTRO" >/dev/null 2>&1 9>&- &
|
||||
INTRO_PREPARE_PID=$!
|
||||
shell_ipc shell prepareThemeIntro "$OLD_BACKGROUND_SNAPSHOT" "$(stat -Lc '%d:%i' "$NEXT_THEME_PATH")" "" "" || true
|
||||
elif background_transition_uses_snapshots "$CHOSEN_THEME_BACKGROUND"; then
|
||||
OLD_BACKGROUND_SNAPSHOT=$(snapshot_current_background)
|
||||
|
||||
# Hand the shell the next background now, so it decodes while the
|
||||
@@ -389,6 +454,12 @@ fi
|
||||
# Generate dynamic configs
|
||||
omarchy-theme-set-templates
|
||||
|
||||
# Replacing the theme's still can cancel an active OWE intro even before the
|
||||
# background link changes. Present its cover before replacing either path.
|
||||
if [[ -n $THEME_BACKGROUND_INTRO ]]; then
|
||||
wait_for_theme_intro themeIntroCoverStatus "$(stat -Lc '%d:%i' "$NEXT_THEME_PATH")" "loading"
|
||||
fi
|
||||
|
||||
# Swap next theme in as current
|
||||
rm -rf "$CURRENT_THEME_PATH"
|
||||
mv "$NEXT_THEME_PATH" "$CURRENT_THEME_PATH"
|
||||
@@ -418,7 +489,6 @@ flock -u 9
|
||||
|
||||
post_theme_commands=(
|
||||
omarchy-restart-terminal
|
||||
omarchy-restart-hyprctl
|
||||
omarchy-restart-btop
|
||||
omarchy-restart-opencode
|
||||
omarchy-restart-helix
|
||||
@@ -437,6 +507,14 @@ post_theme_commands=(
|
||||
)
|
||||
|
||||
if [[ $THEME_HEADLESS != "1" ]]; then
|
||||
# Let the wallpaper and bar finish their reveal before app retints compete
|
||||
# with decoding and the shell's frame rendering. An intro reloads Hyprland
|
||||
# after playback; ordinary theme changes reload it with the app retints.
|
||||
if [[ -n $THEME_INTRO_TOKEN ]]; then
|
||||
wait_for_theme_intro themeIntroStatus "$THEME_INTRO_TOKEN" "pending"
|
||||
else
|
||||
post_theme_commands+=(omarchy-restart-hyprctl)
|
||||
fi
|
||||
run_parallel "${post_theme_commands[@]}"
|
||||
|
||||
# Call hook on theme set
|
||||
|
||||
@@ -29,6 +29,8 @@ if [[ -n $OMARCHY_THEME_SYNC_FROM ]] || ! omarchy-toggle-enabled herdr-theme-syn
|
||||
fi
|
||||
|
||||
theme=$(cat "$HOME/.local/state/omarchy/current/theme.name" 2>/dev/null)
|
||||
control_dir="$XDG_RUNTIME_DIR/omarchy-theme-sync"
|
||||
mkdir -m 700 -p "$control_dir"
|
||||
|
||||
# Runs on the remote machine under a login shell, so OMARCHY_PATH and PATH are set there.
|
||||
# SSH carries no desktop session, so the environment comes from the live Hyprland instance.
|
||||
@@ -66,10 +68,13 @@ read -r -d '' REMOTE_SCRIPT <<'EOF'
|
||||
} </dev/null
|
||||
EOF
|
||||
|
||||
# One machine at a time, so an SSH agent like 1Password asks for approval at most once.
|
||||
# Reuse each authenticated connection for ten idle minutes, so an SSH agent like
|
||||
# 1Password does not need to approve every theme change. Connect one machine at a time.
|
||||
# SSH joins its arguments into a shell command, so the theme travels on stdin as a quoted assignment.
|
||||
for target in $targets; do
|
||||
result=$(printf 'theme=%q from=%q\n%s\n' "$theme" "$(hostname)" "$REMOTE_SCRIPT" |
|
||||
timeout 60 ssh -o BatchMode=yes -o ConnectTimeout=5 "$target" bash -l -s 2>&1 | tail -n 1)
|
||||
timeout 60 ssh -o BatchMode=yes -o ConnectTimeout=5 \
|
||||
-o ControlMaster=auto -o ControlPersist=600 -o "ControlPath=$control_dir/%C" \
|
||||
"$target" bash -l -s 2>&1 | tail -n 1)
|
||||
echo "$(date '+%F %T') $target: ${result:-unreachable}"
|
||||
done
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install and configure Voxtype dictation
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -e
|
||||
|
||||
# Install voxtype and configure it for use.
|
||||
|
||||
if gum confirm "Install Voxtype + AI model (~150MB) to enable dictation?"; then
|
||||
omarchy-pkg-add wtype voxtype-bin
|
||||
|
||||
# Setup voxtype
|
||||
mkdir -p ~/.config/voxtype
|
||||
cp "$OMARCHY_PATH/default/voxtype/config.toml" ~/.config/voxtype/
|
||||
|
||||
voxtype setup --download --no-post-install
|
||||
if omarchy-hw-vulkan; then
|
||||
voxtype setup gpu --enable || true
|
||||
fi
|
||||
voxtype setup systemd
|
||||
|
||||
hyprctl reload >/dev/null
|
||||
omarchy-restart-shell
|
||||
omarchy-notification-send -g "$(omarchy-text "Voxtype Dictation Ready")" "$(omarchy-text "Hold F9 to dictate (or toggle with Super + Ctrl + X).")" -t 10000
|
||||
fi
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Stream voxtype --follow status as bar-friendly JSON
|
||||
|
||||
if omarchy-cmd-missing voxtype; then
|
||||
echo '{"alt": "", "class": "idle", "tooltip": ""}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Keep the status follower as the direct child of the bar. Quickshell
|
||||
# clean up their direct child when they restart; a shell pipeline/coproc would
|
||||
# leave `voxtype status --follow` orphaned under the user systemd instance.
|
||||
exec setpriv --pdeathsig TERM voxtype status --follow --extended --format json
|
||||
@@ -1,5 +1,5 @@
|
||||
--ozone-platform=wayland
|
||||
--ozone-platform-hint=wayland
|
||||
--password-store=gnome-libsecret
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation
|
||||
--enable-features=TouchpadOverscrollHistoryNavigation,OverlayScrollbar
|
||||
--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
|
||||
@@ -0,0 +1 @@
|
||||
superwhisper
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# To put it into use, remove .sample from this file name.
|
||||
|
||||
# Example: add an Include line above [core].
|
||||
# Example: add an Include line above the first repository, [omarchy].
|
||||
# Maintain the repo entries in /etc/pacman.d/custom-repos.conf yourself.
|
||||
|
||||
CONF=/etc/pacman.conf
|
||||
@@ -21,4 +21,4 @@ MARKER="Include = $SNIPPET"
|
||||
[[ -r $SNIPPET ]] || exit 0
|
||||
grep -qxF "$MARKER" "$CONF" && exit 0
|
||||
|
||||
sudo sed -i "0,/^\[core\]/s||$MARKER\n\n[core]|" "$CONF"
|
||||
sudo sed -i "0,/^\[omarchy\]/s||$MARKER\n\n[omarchy]|" "$CONF"
|
||||
@@ -6,12 +6,16 @@ hl.layer_rule({ match = { namespace = "omarchy-bar" }, no_anim = true, animation
|
||||
|
||||
-- Launcher, image selector, emojis, clipboard overlays, the OSD, reminders, the
|
||||
-- Wi-Fi QR code and keyboard-driven panels should pop without compositor layer
|
||||
-- animations. The overlays stay mapped between opens and grow from a parked
|
||||
-- 1x1 when shown, which Hyprland would otherwise animate as a slide in from
|
||||
-- the corner. Panels keep their own QML opacity transition for normal
|
||||
-- open/close, and skip it for panel handoff.
|
||||
-- animations. Overlays open as soon as their scale is ready, without a
|
||||
-- compositor fade or slide. Panels keep their own QML opacity transition
|
||||
-- for normal open/close, and skip it for panel handoff.
|
||||
hl.layer_rule({ match = { namespace = "^(omarchy-menu|omarchy-image-selector|omarchy-emojis|omarchy-clipboard|omarchy-keyboard-panel|omarchy-osd|omarchy-reminders|omarchy-network-qr)$" }, no_anim = true, animation = "none" })
|
||||
|
||||
-- A boot intro hands the desktop between OWE's video layer and the shell's
|
||||
-- background layer. The media fades itself, so a compositor fade on either
|
||||
-- layer only dips the screen toward the empty desktop during the handoff.
|
||||
hl.layer_rule({ match = { namespace = "^(omarchy-background|owe-background)$" }, no_anim = true, animation = "none" })
|
||||
|
||||
-- Dev gallery is the main shell workbench; open it maximized like
|
||||
-- SUPER+ALT+F so component previews have the whole workspace.
|
||||
o.window({ class = "^org.quickshell$", title = "^Omarchy shell – dev gallery$" }, { maximize = true })
|
||||
@@ -1,4 +1,12 @@
|
||||
require("default.hypr.startup-cursor")
|
||||
|
||||
hl.on("hyprland.start", function()
|
||||
-- Applications inherit the user's cursor; only the compositor starts blank.
|
||||
if omarchy_startup_cursor_pending then
|
||||
hl.env("XCURSOR_PATH", omarchy_startup_cursor.path)
|
||||
hl.env("XCURSOR_THEME", omarchy_startup_cursor.xcursor)
|
||||
end
|
||||
|
||||
-- Slow app launch fix -- set systemd vars before starting session services.
|
||||
hl.exec_cmd("systemctl --user import-environment $(env | cut -d'=' -f 1)")
|
||||
hl.exec_cmd("dbus-update-activation-environment --systemd --all")
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
if o.shell_succeeds("omarchy-default-dictation") then
|
||||
o.bind("SUPER + CTRL + X", "Toggle dictation", "omarchy-dictation toggle")
|
||||
o.bind("F9", "Start dictation (push-to-talk)", "omarchy-dictation start")
|
||||
o.bind("F9", "Stop dictation (push-to-talk)", "omarchy-dictation stop", { release = true })
|
||||
-- A modifier's mask changes between its press and release. Match the keysym
|
||||
-- independently of that mask; AltGr layouts use a different keysym.
|
||||
o.bind("ALT + Alt_R", "Start dictation (push-to-talk)", "omarchy-dictation start", { ignore_mods = true })
|
||||
o.bind("ALT + Alt_R", "Stop dictation (push-to-talk)", "omarchy-dictation stop", { release = true, ignore_mods = true })
|
||||
end
|
||||
@@ -1,5 +0,0 @@
|
||||
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 })
|
||||
end
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
-- Backends may supply their own desktop integration without editing the
|
||||
-- user's Hyprland config. Recording bindings remain owned by Omarchy.
|
||||
local paths = require("default.hypr.paths")
|
||||
local pipe = io.popen("omarchy-default-dictation 2>/dev/null", "r")
|
||||
if not pipe then
|
||||
return
|
||||
end
|
||||
|
||||
local backend = pipe:read("*l")
|
||||
pipe:close()
|
||||
|
||||
if not backend or not backend:match("^[%w%-]+$") or o.cmd_missing("omarchy-dictation-" .. backend) then
|
||||
return
|
||||
end
|
||||
|
||||
local integration = paths.config_home .. "/" .. backend .. "/shortcuts.lua"
|
||||
local file = io.open(integration, "r")
|
||||
if file then
|
||||
file:close()
|
||||
local ok, error = pcall(dofile, integration)
|
||||
if not ok then
|
||||
print("Could not load dictation backend " .. backend .. ": " .. tostring(error))
|
||||
end
|
||||
end
|
||||
@@ -104,6 +104,7 @@ hl.config({
|
||||
misc = {
|
||||
disable_hyprland_logo = true,
|
||||
disable_splash_rendering = true,
|
||||
background_color = "rgb(000000)",
|
||||
disable_scale_notification = true,
|
||||
focus_on_activate = true,
|
||||
anr_missed_pings = 3,
|
||||
|
||||
@@ -12,7 +12,7 @@ if _G.omarchy_default_bindings ~= false then
|
||||
require("default.hypr.bindings.jis")
|
||||
require("default.hypr.bindings.tiling")
|
||||
require("default.hypr.bindings.utilities")
|
||||
require("default.hypr.bindings.voxtype")
|
||||
require("default.hypr.bindings.dictation")
|
||||
require_optional.module("default.hypr.bindings.applications")
|
||||
end
|
||||
require("default.hypr.envs")
|
||||
@@ -20,6 +20,7 @@ require("default.hypr.looknfeel")
|
||||
require("default.hypr.qconsole")
|
||||
require("default.hypr.input")
|
||||
require("default.hypr.windows")
|
||||
require("default.hypr.dictation-backend")
|
||||
|
||||
-- Current theme overrides.
|
||||
require_optional.module("omarchy.current.theme.hyprland")
|
||||
@@ -0,0 +1,46 @@
|
||||
-- Hyprland polls cursor.invisible, so its first frames need a blank cursor too.
|
||||
function omarchy_startup_cursor_restore(loaded)
|
||||
if not omarchy_startup_cursor_pending then return end
|
||||
local cursor = omarchy_startup_cursor
|
||||
if loaded then
|
||||
omarchy_startup_cursor_pending = false
|
||||
hl.config({ cursor = cursor.config })
|
||||
if cursor.config.enable_hyprcursor and cursor.hyprcursor then
|
||||
hl.exec_cmd("hyprctl setcursor " .. o.shell_quote(cursor.hyprcursor) .. " " .. cursor.size)
|
||||
end
|
||||
elseif not cursor.restoring then
|
||||
-- Reload the normal Xcursor fallback before enabling Hyprcursor or GSettings.
|
||||
cursor.restoring = true
|
||||
hl.exec_cmd("hyprctl setcursor " .. o.shell_quote(cursor.xcursor) .. " " .. cursor.size
|
||||
.. " && hyprctl eval 'omarchy_startup_cursor_restore(true)'")
|
||||
end
|
||||
end
|
||||
|
||||
hl.on("config.reloaded", function()
|
||||
if omarchy_startup_cursor_pending == nil then
|
||||
-- Config updates in an existing compositor must not hide its pointer.
|
||||
omarchy_startup_cursor_pending = #hl.get_monitors() == 0
|
||||
if omarchy_startup_cursor_pending then
|
||||
local hyprcursor = hl.get_config("cursor.enable_hyprcursor")
|
||||
omarchy_startup_cursor = {
|
||||
config = {
|
||||
invisible = hl.get_config("cursor.invisible"),
|
||||
enable_hyprcursor = hyprcursor,
|
||||
sync_gsettings_theme = hl.get_config("cursor.sync_gsettings_theme"),
|
||||
},
|
||||
hyprcursor = os.getenv("HYPRCURSOR_THEME"),
|
||||
size = tonumber((hyprcursor and os.getenv("HYPRCURSOR_SIZE")) or os.getenv("XCURSOR_SIZE")) or 24,
|
||||
path = os.getenv("XCURSOR_PATH") or "~/.local/share/icons:~/.icons:/usr/share/icons:/usr/share/pixmaps",
|
||||
xcursor = os.getenv("XCURSOR_THEME") or "default",
|
||||
}
|
||||
if omarchy_startup_cursor.size <= 0 then omarchy_startup_cursor.size = 24 end
|
||||
hl.env("XCURSOR_PATH", os.getenv("OMARCHY_PATH") .. "/default/hypr/cursors:" .. omarchy_startup_cursor.path)
|
||||
hl.env("XCURSOR_THEME", "omarchy-startup")
|
||||
end
|
||||
end
|
||||
if omarchy_startup_cursor_pending then
|
||||
-- Keep the temporary cursor private to the compositor, including GSettings.
|
||||
hl.config({ cursor = { invisible = true, enable_hyprcursor = false, sync_gsettings_theme = false } })
|
||||
hl.timer(function() omarchy_startup_cursor_restore() end, { timeout = 15000, type = "oneshot" })
|
||||
end
|
||||
end)
|
||||
@@ -163,6 +163,9 @@
|
||||
"setup.default.browser.edge": {"icon":"","label":"Edge","checked":"[[ \"$(omarchy-default-browser)\" == \"edge\" ]]","action":"omarchy-default-browser edge"},
|
||||
"setup.default.browser.firefox": {"icon":"","label":"Firefox","checked":"[[ \"$(omarchy-default-browser)\" == \"firefox\" ]]","action":"omarchy-default-browser firefox"},
|
||||
"setup.default.browser.zen": {"icon":"","label":"Zen","checked":"[[ \"$(omarchy-default-browser)\" == \"zen\" ]]","action":"omarchy-default-browser zen"},
|
||||
"setup.default.dictation": {"icon":"","label":"Dictation","title":"Default Dictation"},
|
||||
"setup.default.dictation.superwhisper": {"icon":"","label":"Superwhisper","checked":"[[ \"$(omarchy-default-dictation)\" == \"superwhisper\" ]]","action":"omarchy-default-dictation superwhisper"},
|
||||
"setup.default.dictation.voxtype": {"icon":"","label":"Voxtype","checked":"[[ \"$(omarchy-default-dictation)\" == \"voxtype\" ]]","action":"omarchy-default-dictation voxtype"},
|
||||
"setup.default.terminal": {"icon":"","label":"Terminal","title":"Default Terminal"},
|
||||
"setup.default.terminal.alacritty": {"icon":"","label":"Alacritty","checked":"[[ \"$(omarchy-default-terminal)\" == \"alacritty\" ]]","action":"omarchy-default-terminal alacritty"},
|
||||
"setup.default.terminal.foot": {"icon":"","label":"Foot","checked":"[[ \"$(omarchy-default-terminal)\" == \"foot\" ]]","action":"omarchy-default-terminal foot"},
|
||||
@@ -267,7 +270,6 @@
|
||||
"install.terminal.kitty": {"icon":"","label":"Kitty","disabled":"omarchy-pkg-present kitty","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-terminal kitty'"},
|
||||
"install.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","disabled":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-chatgpt"},
|
||||
"install.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","disabled":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-claude"},
|
||||
"install.ai.dictation": {"icon":"","label":"Dictation","disabled":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install"},
|
||||
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","disabled":"omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
|
||||
"install.ai.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes Desktop","disabled":"omarchy-pkg-present hermes-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-hermes"},
|
||||
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","disabled":"omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
|
||||
@@ -311,6 +313,7 @@
|
||||
// Remove
|
||||
"remove.package": {"icon":"","label":"Package","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-remove"},
|
||||
"remove.ai": {"icon":"","label":"AI","title":"Remove"},
|
||||
"remove.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin || omarchy-pkg-present superwhisper-bin"},
|
||||
"remove.service": {"icon":"","label":"Services","title":"Remove"},
|
||||
"remove.development": {"icon":"","label":"Development","title":"Remove"},
|
||||
"remove.theme": {"icon":"","label":"Theme","action":"omarchy-theme-remove"},
|
||||
@@ -338,7 +341,8 @@
|
||||
"remove.service.ssh-agent": {"icon":"","label":"SSH Agent","when":"systemctl --user -q is-enabled gcr-ssh-agent.socket","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-service-ssh-agent"},
|
||||
"remove.ai.chatgpt": {"icon":"","iconFont":"omarchy","label":"ChatGPT Desktop","when":"omarchy-pkg-present openai-codex-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-chatgpt"},
|
||||
"remove.ai.claude": {"icon":"","iconFont":"omarchy","label":"Claude Desktop","when":"omarchy-pkg-present claude-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-claude"},
|
||||
"remove.ai.dictation": {"icon":"","label":"Dictation","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-remove"},
|
||||
"remove.dictation.superwhisper": {"icon":"","label":"Superwhisper","when":"omarchy-pkg-present superwhisper-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-dictation-superwhisper"},
|
||||
"remove.dictation.voxtype": {"icon":"","label":"Voxtype","when":"omarchy-pkg-present voxtype-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-dictation-voxtype"},
|
||||
"remove.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"omarchy-pkg-present grok-bot","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-grok-bot"},
|
||||
"remove.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"omarchy-pkg-present lmstudio-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-lm-studio"},
|
||||
"remove.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"omarchy-pkg-present ollama","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-ollama"},
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
"setup.network.qr": {"label":"二维码"},
|
||||
"setup.default": {"label":"默认程序"},
|
||||
"setup.default.agent": {"title":"默认 Agent"},
|
||||
"setup.default.dictation": {"label":"语音听写","title":"默认语音听写"},
|
||||
"setup.default.browser": {"label":"浏览器","title":"默认浏览器"},
|
||||
"setup.default.terminal": {"label":"终端","title":"默认终端"},
|
||||
"setup.default.editor": {"label":"编辑器","title":"默认编辑器"},
|
||||
@@ -125,7 +126,6 @@
|
||||
"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 启动器"},
|
||||
@@ -142,7 +142,7 @@
|
||||
"remove.security": {"label":"安全","title":"移除"},
|
||||
"remove.security.fingerprint": {"label":"指纹"},
|
||||
"remove.security.sudoless-docker": {"label":"免 sudo Docker"},
|
||||
"remove.ai.dictation": {"label":"语音听写"},
|
||||
"remove.dictation": {"label":"语音听写"},
|
||||
"remove.gaming.xbox-cloud": {"label":"Xbox 云游戏"},
|
||||
"remove.gaming.xbox-controllers": {"label":"Xbox 手柄 ()"},
|
||||
"remove.development.javascript": {"title":"移除"},
|
||||
|
||||
@@ -16,6 +16,9 @@ SigLevel = Required DatabaseOptional
|
||||
LocalFileSigLevel = Optional
|
||||
|
||||
# pacman searches repositories in the order defined here
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/edge/$arch
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
@@ -25,9 +28,6 @@ Include = /etc/pacman.d/mirrorlist
|
||||
[multilib]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/edge/$arch
|
||||
|
||||
# Repositories for debug symbol packages.
|
||||
#
|
||||
# These upstream debug packages will only match core/extra/multilib packages from the Omarchy edge
|
||||
|
||||
@@ -16,6 +16,9 @@ SigLevel = Required DatabaseOptional
|
||||
LocalFileSigLevel = Optional
|
||||
|
||||
# pacman searches repositories in the order defined here
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/rc/$arch
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
@@ -24,6 +27,3 @@ Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[multilib]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/rc/$arch
|
||||
@@ -16,6 +16,9 @@ SigLevel = Required DatabaseOptional
|
||||
LocalFileSigLevel = Optional
|
||||
|
||||
# pacman searches repositories in the order defined here
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/stable/$arch
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
@@ -24,6 +27,3 @@ Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[multilib]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[omarchy]
|
||||
Server = https://pkgs.omarchy.org/stable/$arch
|
||||
@@ -53,6 +53,7 @@ hl.config({
|
||||
misc = {
|
||||
disable_hyprland_logo = true,
|
||||
disable_splash_rendering = true,
|
||||
background_color = "rgb(000000)",
|
||||
force_default_wallpaper = 0,
|
||||
},
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
state_file = "auto"
|
||||
|
||||
[hotkey]
|
||||
# Hotkey is configured in Hyprland. Default is Super + Ctrl + X
|
||||
# Hotkeys are configured in Hyprland: hold Right Alt or F9, or toggle with Super + Ctrl + X.
|
||||
enabled = false
|
||||
|
||||
[audio]
|
||||
|
||||
+2
-2
@@ -267,8 +267,8 @@ first runs `omarchy-provision-user || true` so finalize catches up if it
|
||||
never ran, then handles the steps that need a running graphical session
|
||||
and/or a working user systemd instance:
|
||||
|
||||
- `omarchy-hook-install post-update` for the three shipped hooks
|
||||
(`install-voxtype.hook`, `setup-fingerprint.hook`, `setup-agent.hook`).
|
||||
- `omarchy-hook-install post-update` for the two shipped hooks
|
||||
(`setup-fingerprint.hook`, `setup-agent.hook`).
|
||||
- `install/user/first-run/enable-user-units.sh` — daemon-reload, then
|
||||
`systemctl --user enable --now` the shipped user units (`bt-agent`,
|
||||
`omarchy-sleep-lock`, `omarchy-recover-internal-monitor`,
|
||||
|
||||
+21
-7
@@ -189,6 +189,20 @@ overrides like `omarchy display text size` survive theme switches.
|
||||
|
||||
## Theme tokens
|
||||
|
||||
Shell and plugin QML must qualify the palette singleton because Qt 6.12 introduces its own `Color` type. Import `qs.Commons` with an alias and use `Commons.Color` for palette properties and signal connections:
|
||||
|
||||
```qml
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Rectangle {
|
||||
color: Commons.Color.background
|
||||
}
|
||||
```
|
||||
|
||||
The unqualified import can remain for `Style`, `Util`, and `Border`. Existing third-party plugins that use bare `Color` must make the same change; fixing the host shell does not change a plugin's import scope.
|
||||
|
||||
See [`theming.md`](theming.md) for the full theme/template workflow,
|
||||
including generated `*.tpl` files, gradient helpers, and shell border syntax.
|
||||
|
||||
@@ -200,17 +214,17 @@ or override a single section with `shell.<section>.toml`, merged in by
|
||||
`omarchy-theme-set-templates` (see [`theming.md`](theming.md)).
|
||||
|
||||
`colors.toml` uses `foreground` and `background` for the foundational
|
||||
text/background palette, exposed to QML as `Color.foreground` and
|
||||
`Color.background`.
|
||||
text/background palette, exposed to QML as `Commons.Color.foreground` and
|
||||
`Commons.Color.background`.
|
||||
|
||||
The shell exposes these tokens to QML via three singletons in
|
||||
`qs.Commons`:
|
||||
|
||||
- `Color` — palette (`foreground`, `background`, `accent`, `urgent`)
|
||||
and per-surface roles (`Color.bar.*`, `Color.popups.*`,
|
||||
`Color.tooltip.*`, `Color.notifications.*`, `Color.menu.*`,
|
||||
`Color.polkit.*`, `Color.lock.*`, `Color.imagePicker.*`). Clipboard
|
||||
and emojis share `Color.menu.*`; the `[launcher]` section is consumed
|
||||
and per-surface roles (`Commons.Color.bar.*`, `Commons.Color.popups.*`,
|
||||
`Commons.Color.tooltip.*`, `Commons.Color.notifications.*`, `Commons.Color.menu.*`,
|
||||
`Commons.Color.polkit.*`, `Commons.Color.lock.*`, `Commons.Color.imagePicker.*`). Clipboard
|
||||
and emojis share `Commons.Color.menu.*`; the `[launcher]` section is consumed
|
||||
by the launcher outside shell QML.
|
||||
- `Style` — structural tokens (`cornerRadius`), shared interactive
|
||||
state tokens/helpers, spacing (`Style.spacing.*` / `Style.space(px)`),
|
||||
@@ -218,7 +232,7 @@ The shell exposes these tokens to QML via three singletons in
|
||||
(`Style.bar.sizeHorizontal` / `Style.bar.sizeVertical`).
|
||||
- `Border` — border-spec helpers for QML surfaces. Use with
|
||||
`BorderSurface` from `qs.Ui` when a border should honor shell theme
|
||||
gradients or per-side widths. `Color.<section>.border` is only the
|
||||
gradients or per-side widths. `Commons.Color.<section>.border` is only the
|
||||
flat-color fallback for code that cannot render a real border.
|
||||
|
||||
### Interactive states
|
||||
|
||||
@@ -36,6 +36,10 @@ For China, the builder installs `archlinuxcn-keyring` using the existing Arch tr
|
||||
|
||||
The China profile provides repository defaults only. Language is a separate installer choice, not part of a region: a Chinese speaker outside China keeps global mirrors, and a China install in English still gets the regional ones.
|
||||
|
||||
## Additions on the omarchycn branch
|
||||
|
||||
The `cn` profile here also carries developer mirrors: `environment.conf`, `cargo.toml`, `gemrc` and `maven-settings.xml`. `install/config/region.sh` links the first three into `/etc` and `/.cargo` for `cn`; Maven reads `maven-settings.xml` in place through the `MAVEN_ARGS` that `environment.conf` sets. The script removes only its own links for `global` and refuses to replace a regular file it did not create. It reads the marker during installation; `omarchy region cn|global` passes the region it switches to. The region also gates _Install > China Apps_ and _Setup > Claude Code API_. Language stays out of every region: `omarchy language en|zh` owns `LANG`.
|
||||
|
||||
## Remaining release validation
|
||||
|
||||
Unit tests cover region/channel independence, the timezone mapping, generated pacman contents, repeat application, and refresh ordering; refresh itself is exercised only inside the sudo-boundary sandbox. They do not establish live mirror availability, real package trust, or successful offline installation. Before distribution, build from matching local checkouts on an x86_64 builder and run the disposable-VM acceptance workflow in `agents/skills/acceptance-tests.md`.
|
||||
|
||||
+10
-10
@@ -6,12 +6,11 @@ Omarchy themes live under `themes/<name>/` in the source tree (installed at
|
||||
`colors.toml`; Omarchy generates the active theme files from
|
||||
`default/themed/*.tpl` when `omarchy-theme-set <name>` runs.
|
||||
|
||||
Beyond `colors.toml` and hand-written config overrides, a first-party theme can
|
||||
ship `backgrounds/` (users overlay their own via
|
||||
`~/.config/omarchy/backgrounds/<name>/`; the active image is the
|
||||
`~/.local/state/omarchy/current/background` symlink), `preview.png` and
|
||||
`preview-unlock.png` for the theme switcher, `icons.theme`, `keyboard.rgb`,
|
||||
`unlock.png`, and a `light.mode` marker file.
|
||||
Beyond `colors.toml` and hand-written config overrides, a first-party theme can ship `backgrounds/` (users overlay their own via `~/.config/omarchy/backgrounds/<name>/`; the active image is the `~/.local/state/omarchy/current/background` symlink), `preview.png` and `preview-unlock.png` for the theme switcher, `icons.theme`, `keyboard.rgb`, `unlock.png`, and a `light.mode` marker file.
|
||||
|
||||
A theme can pair a still background with a silent intro video using matching filenames: `backgrounds/0-winding-road.webp` and `backgrounds/intros/0-winding-road.mp4`. The video plays automatically at login and when switching to that theme with this background selected. No registration, checksum file or manual setting is needed. Supported video extensions are `mp4`, `m4v`, `mov`, `webm`, `mkv` and `avi`. Intro videos stay inside the nested `intros/` directory so the background picker does not list them as looping wallpapers. An image selected from another directory looks for its intro in that directory's own `intros/`, so a same-named custom image cannot inherit a theme's video.
|
||||
|
||||
Aim for a video between five and seven seconds long that opens on its scene setup and ends on the matching still. The shell provides the opening crossfade, so an added fade from black is unnecessary. OWE owns decoding, mute and the handoff back to that image. The theme picker prepares the highlighted theme's remembered intro offscreen, paused at its opening frame. OWE keeps one prepared clip and an idle renderer for up to one minute, then releases them. A theme switch also starts preparation while its templates render; OWE compares the selected video with the prepared clip before resuming it. Older OWE versions use the ordinary startup path. Hyprland and the SDDM greeter use a black compositor background. On a fresh login, the persistent shell host keeps an opaque black cover above the bar and wallpaper until the bar has loaded and OWE has a moving video frame, then fades both in together over 420 ms. Hyprland initializes with a transparent cursor so its visibility polling cannot expose a pointer in the first frames. The temporary Xcursor theme stays private to the compositor; applications inherit the normal cursor environment. The opening fade restores the selected cursor and settings. Shell restarts and a fifteen-second compositor timeout recover a cursor left hidden by an interrupted startup. With intros disabled or unavailable, it waits for the still to be ready instead. A ten-second deadline releases the cover if a missing wallpaper or failed plugin never becomes ready. Ordinary shell restarts preload the selected still alongside the bar without mapping that login cover. Login intros use the Hyprland instance signature, so logging out and back in plays the intro again. The launcher consumes the current session before waiting up to five seconds for OWE. Shell restarts and a daemon starting after that window cannot play a delayed intro. Theme switches synchronize OWE with the selected image and use its first-frame start mode. An interrupted video supplies its currently playing frame as the outgoing snapshot. Rapid selections wait for the previous opening fade to finish before capturing it. Theme replacement and playback wait for the outgoing cover to decode and reach the compositor on every screen. A persistent layer holds that outgoing wallpaper until OWE hides the shell background and reports a ready video with an advancing playback position and no outgoing still transition, then crossfades it over 420 ms while applying the new bar palette. The shell retains its background service and decoded wallpaper while it is hidden, so handing the still back does not recreate them. App retints wait until the opening crossfade completes, with a three-second bound, so they do not compete with its first frames. The compositor reload waits until playback finishes because reloading Hyprland pauses video presentation. A superseded intro skips that reload. Failed playback releases the pending palette, and a newer theme invalidates the older handoff. Refreshing a theme, headless setup, and cycling backgrounds within the same theme do not trigger intros. Toggle > Animations suppresses playback along with the other animations. The `omarchy theme bg intro toggle` command can toggle intros separately.
|
||||
|
||||
A theme installed from a git repo is held to a much shorter list; see [What an installed theme may not ship](#what-an-installed-theme-may-not-ship).
|
||||
|
||||
@@ -198,7 +197,7 @@ The filename decides the target section, so the `[lock]` header is optional.
|
||||
|
||||
The running shell reads `shell.toml` into two QML singletons:
|
||||
|
||||
- `Color` for palette and surface roles like `Color.menu.border`.
|
||||
- `Color` for palette and surface roles like `Commons.Color.menu.border`.
|
||||
- `Style` for controls, spacing, font scale, corner radius, and bar sizing.
|
||||
|
||||
### Borders
|
||||
@@ -327,11 +326,12 @@ Plugin and shell QML should use `BorderSurface` for theme-aware borders:
|
||||
|
||||
```qml
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
BorderSurface {
|
||||
color: Color.popups.background
|
||||
borderSpec: Border.surfaceSpec("popups", "border", Color.popups.border, 2)
|
||||
color: Commons.Color.popups.background
|
||||
borderSpec: Border.surfaceSpec("popups", "border", Commons.Color.popups.border, 2)
|
||||
padding: Style.spacing.popupPadding
|
||||
|
||||
Item {
|
||||
@@ -348,7 +348,7 @@ Use `Border.surfaceSpec(section, token, fallbackColor, fallbackWidth, alphaKey)`
|
||||
for shell theme tokens (the optional `alphaKey` names the alpha token, e.g.
|
||||
`"border-alpha"`), `Border.controlSpec(state, foreground, accent, urgent)` for
|
||||
shared controls, and `Border.flat(color, width)` for a deliberate local border
|
||||
that should not be overridden by the active theme. `Color.<section>.border` is the
|
||||
that should not be overridden by the active theme. `Commons.Color.<section>.border` is the
|
||||
flat first-stop color for consumers that cannot render full border specs.
|
||||
|
||||
## Hyprland templates
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[Service]
|
||||
# Let start-hyprland stop the compositor before killing its shell clients.
|
||||
# Sending SIGTERM to the whole group removes the wallpaper during teardown.
|
||||
KillMode=mixed
|
||||
@@ -131,6 +131,7 @@ sddm
|
||||
slurp
|
||||
socat
|
||||
starship
|
||||
superwhisper-bin
|
||||
sushi
|
||||
system-config-printer
|
||||
tesseract
|
||||
|
||||
@@ -151,7 +151,7 @@ omarchy_prompt_language() {
|
||||
omarchy_prompt_keyboard() {
|
||||
local choice status selected=${1:-English (US)}
|
||||
choice=$(printf '%s\n' "$OMARCHY_KEYBOARD_LAYOUTS" | cut -d'|' -f1 |
|
||||
gum choose --height 10 --selected "$selected" --header "$(omarchy_text "Select keyboard layout")") && status=0 || status=$?
|
||||
gum choose --height 10 --selected "${selected//,/\\,}" --header "$(omarchy_text "Select keyboard layout")") && status=0 || status=$?
|
||||
((status == 0)) || return $status
|
||||
|
||||
local selection
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if omarchy-done ensure voxtype-install-invitation; then
|
||||
omarchy-notification-send -u critical -g "$(omarchy-text "Install Dictation with Voxtype")" \
|
||||
"$(omarchy-text "Click to install voice dictation for Omarchy.")" \
|
||||
--exec omarchy-launch-floating-terminal-with-presentation omarchy-voxtype-install
|
||||
fi
|
||||
@@ -12,7 +12,12 @@ case ${OMARCHY_SETUP_CONTEXT:-runtime} in
|
||||
esac
|
||||
|
||||
if [[ -n $NODE_PACKAGE_DIR ]]; then
|
||||
NODE_TARBALL=$(find "$NODE_PACKAGE_DIR" -name "node-v*-linux-x64.tar.gz" -type f 2>/dev/null | head -n1)
|
||||
# The ISO bundles the tarball for its own architecture.
|
||||
case $(uname -m) in
|
||||
aarch64) NODE_PLATFORM=linux-arm64 ;;
|
||||
*) NODE_PLATFORM=linux-x64 ;;
|
||||
esac
|
||||
NODE_TARBALL=$(find "$NODE_PACKAGE_DIR" -name "node-v*-$NODE_PLATFORM.tar.gz" -type f 2>/dev/null | head -n1)
|
||||
if [[ -z $NODE_TARBALL ]]; then
|
||||
if [[ ${OMARCHY_SETUP_CONTEXT:-} == "provision-owner" ]]; then
|
||||
# A factory snapshot predating the bundled tarball may not have it staged.
|
||||
@@ -24,7 +29,7 @@ if [[ -n $NODE_PACKAGE_DIR ]]; then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
NODE_VERSION=$(basename "$NODE_TARBALL" | sed 's/node-v\(.*\)-linux-x64.tar.gz/\1/')
|
||||
NODE_VERSION=$(basename "$NODE_TARBALL" | sed "s/node-v\(.*\)-$NODE_PLATFORM.tar.gz/\1/")
|
||||
NODE_INSTALL_DIR="$HOME/.local/share/mise/installs/node/$NODE_VERSION"
|
||||
|
||||
mkdir -p "$NODE_INSTALL_DIR"
|
||||
|
||||
@@ -22,11 +22,11 @@ The full-disk encryption won't allow you to enter the password from a Bluetooth
|
||||
|
||||
### Installing in China
|
||||
|
||||
The installer's first screen asks for your region. Pick _China_ for Chinese package mirrors, a Chinese system language and input method, and Chinese developer mirrors and apps. See [China](52-china.md).
|
||||
The installer's first screen asks for your language; pick _Chinese_ for a Chinese installer and system. The timezone you pick later decides the region: `Asia/Shanghai` brings Chinese package and developer mirrors and the China apps. See [China](52-china.md).
|
||||
|
||||
### Installing for another owner
|
||||
|
||||
If you're setting up a machine for someone else — a family member, a new employee, a buyer — you shouldn't be answering the personal questions on their behalf. Hit `Ctrl + C` on the very first screen of the installer (the region and keyboard selection), and Omarchy will offer to prepare the machine for another owner instead. The system installs right away, but all the personal setup — keyboard layout, username, password — is deferred until the machine boots for the first time. The drive is still encrypted by default, and the password the new owner picks on that first boot becomes the encryption password too. (A machine you've already been using can be handed over without a reinstall too — see [resetting the computer](48-security.md).)
|
||||
If you're setting up a machine for someone else — a family member, a new employee, a buyer — you shouldn't be answering the personal questions on their behalf. Hit `Ctrl + C` on the very first screen of the installer (the language and keyboard selection), and Omarchy will offer to prepare the machine for another owner instead. The system installs right away, but all the personal setup — keyboard layout, username, password — is deferred until the machine boots for the first time. The drive is still encrypted by default, and the password the new owner picks on that first boot becomes the encryption password too. (A machine you've already been using can be handed over without a reinstall too — see [resetting the computer](48-security.md).)
|
||||
|
||||
### Unattended installs
|
||||
|
||||
|
||||
@@ -150,8 +150,8 @@ Usually on Linux, you need `Ctrl + Shift + C/V` to copy'n'paste in the terminal
|
||||
| `Super + Alt + ]` | Make webcam overlay larger while recording |
|
||||
| `Alt + Shift + L` | Copy current URL from webapp or Chromium |
|
||||
| `Alt + Shift + D` | Download the video on the current page to `~/Videos` |
|
||||
| `Super + Ctrl + X` | Start/stop dictation (requires _Install > AI > Dictation_) |
|
||||
| `F9` | Push-to-talk dictation (requires _Install > AI > Dictation_) |
|
||||
| `Super + Ctrl + X` | Start/stop dictation (requires _Setup > Defaults > Dictation_) |
|
||||
| `Right Alt` / `F9` | Push-to-talk dictation (requires Voxtype or Superwhisper) |
|
||||
|
||||
With screenrecordings, the hotkey first asks which audio you want, then starts recording. Hit it again to stop. See [screenshots and recording](12-screenshots-recording.md) for the details.
|
||||
|
||||
|
||||
@@ -10,6 +10,14 @@ This is very helpful for grabbing addresses out of image footers or phone number
|
||||
|
||||
### Dictation
|
||||
|
||||
Omarchy offers AI dictation via [Voxtype](https://voxtype.io/). You install it via _Install > AI > Dictation_ through the Omarchy menu. By default, it'll load a base English model that takes up 150MB. But you can tweak which model you'd like to use by running `voxtype setup model` in the terminal. And you can tweak all the settings via `~/.config/voxtype/config.toml`.
|
||||
On fresh installs, [Superwhisper](https://superwhisper.com/) is the default dictation backend, using cloud processing without offline model downloads. Choose Superwhisper or [Voxtype](https://voxtype.io/) through _Setup > Defaults > Dictation_ in the Omarchy menu. Selecting a backend installs it if needed and makes it the default for dictation. Voxtype loads a base English model that takes up 150MB; change the model with `voxtype setup model` and other settings through `~/.config/voxtype/config.toml`. Superwhisper opens its settings to choose cloud or local processing, and OPR keeps the application updated.
|
||||
|
||||
Once installed, you dictate by holding down `F9` or by toggling with `Super + Ctrl + X`, and the dictated text will appear in the focused input area.
|
||||
You dictate by holding down `Right Alt` or `F9`, or by toggling with `Super + Ctrl + X`, and the dictated text will appear in the focused input area. These shortcuts use the selected backend. Right Alt also starts dictation when used in a modifier chord; use Left Alt for those chords. On keyboard layouts where Right Alt is AltGr, use `F9` to keep AltGr available for typing.
|
||||
|
||||
The same shortcuts work with either backend. Select your backend through Setup > Defaults > Dictation. The backend installer configures it and saves the selection. Run `omarchy default dictation` to see which one is selected. The selection is saved in `~/.config/omarchy/defaults/dictation`. Fresh installs select Superwhisper automatically.
|
||||
|
||||
Omarchy loads the selected backend's desktop integration automatically. No Superwhisper configuration is needed in `~/.config/hypr/`.
|
||||
|
||||
Custom bindings and scripts can use `omarchy dictation start`, `omarchy dictation stop`, and `omarchy dictation toggle`. The selected backend handles the recording and transcription. Selecting Superwhisper disables its native hold shortcut and sets its native toggle to `Alt + Space`, keeping it separate from Omarchy's recording keys. Superwhisper requires at least one native recording shortcut, and its cancellation shortcut stays available.
|
||||
|
||||
Additional backends can integrate without changing Omarchy. Provide an executable named `omarchy-dictation-<backend>` on PATH that accepts `start`, `stop`, and `toggle` and returns promptly, with a nonzero status on failure. Its installer configures the provider and calls `omarchy-dictation-use <backend>` after successful setup. This stops the previous backend and saves the selection atomically; `stop` must succeed when the provider is idle or unavailable. Backend names use lowercase letters, digits, and hyphens. Provide `omarchy-install-dictation-<backend>` for setup; `omarchy default dictation <backend>` launches it. Add a Defaults menu entry through an Omarchy menu extension to call that selector. Optional desktop integration can live in `${XDG_CONFIG_HOME:-$HOME/.config}/<backend>/shortcuts.lua`.
|
||||
@@ -6,6 +6,10 @@ You can do this most easily by going to _Install > Style > Background_ in the Om
|
||||
|
||||
Backgrounds can be videos as well as stills. Drop an `mp4`, `m4v`, `mov`, `webm`, `mkv`, or `avi` file in the same folder and it appears alongside the images. Videos are played by the OWE wallpaper engine. It decodes the video once for all monitors and plays its sound through the default audio output, and it stops playback whenever nothing can see it. The lock screen draws the same decode, muted, through OWE. A video wallpaper still costs far more power than a still one.
|
||||
|
||||
Themes can include a short intro for a still background. For `backgrounds/road.webp`, the matching video lives at `backgrounds/intros/road.mp4`. It plays automatically at boot and when switching to a theme with that background selected, then returns to the still. Intro videos stay out of the background picker and never loop.
|
||||
|
||||
Turning off _Toggle > Animations_ also turns off background intros. Omarchy does this by default in a virtual machine. The `omarchy theme bg intro toggle` command can toggle intros separately. Refreshing a theme or choosing another background within the same theme does not play an intro.
|
||||
|
||||
You can find a huge collection of cool curated backgrounds on https://github.com/dharmx/walls.
|
||||
|
||||
Video backgrounds keep a cached still on the lock screen while playback is paused or unavailable. Animated GIFs play on the desktop and show a still frame on the lock screen.
|
||||
@@ -10,7 +10,7 @@ The files are exactly what the installer's own wizard writes, so the easiest way
|
||||
|
||||
| File | Required | Purpose |
|
||||
|------|----------|---------|
|
||||
| `user_configuration.json` | Yes | Disk, hostname, timezone, keyboard, and region (`omarchy_install.region`, `global` or `cn`) |
|
||||
| `user_configuration.json` | Yes | Disk, hostname, timezone (which picks the region unless `omarchy_install.region` names one), keyboard, and language (`locale_config.sys_lang`) |
|
||||
| `user_credentials.json` | Yes | Username and password hash |
|
||||
| `user_full_name.txt` | No | Git full name |
|
||||
| `user_email_address.txt` | No | Git email |
|
||||
|
||||
+19
-13
@@ -1,24 +1,28 @@
|
||||
# China
|
||||
|
||||
Omarchy is built to work in mainland China without a VPN. Pick _China_ on the first screen of the installer, the region picker that comes before the keyboard layout, and the machine comes up ready: Chinese menus and system language, a Chinese input method, Chinese package mirrors, and the Chinese software people actually need.
|
||||
Omarchy works in mainland China without a VPN. Two installer answers set a machine up for China, and each stands on its own:
|
||||
|
||||
## What China sets up
|
||||
- **Language.** The installer's first screen asks for English or Simplified Chinese. Chinese puts the installer, the system language (`LANG=zh_CN.UTF-8`), the menus and the desktop shell in Chinese.
|
||||
- **Region.** The timezone you pick decides the region. `Asia/Shanghai`, `Asia/Urumqi` and their older names select China; any other timezone is Global. The summary screen shows _Region: cn_ before anything touches the disk.
|
||||
|
||||
A Chinese speaker abroad keeps the global mirrors, and an English install in Shanghai still gets the China mirrors.
|
||||
|
||||
## What the China region sets up
|
||||
|
||||
| Area | China | Global |
|
||||
|------|-------|--------|
|
||||
| Omarchy and Arch packages | `pkgs.omarchy.cn` and the `*-mirror.omarchy.cn` mirrors first, the `.org` servers behind them, then USTC; the ArchLinuxCN repository | `.org` servers only |
|
||||
| Language | `LANG=zh_CN.UTF-8`, Chinese menu labels | English |
|
||||
| Input method | Fcitx5 with [Rime Ice](https://github.com/iDvel/rime-ice) and the Wanxiang grammar model; `Ctrl + Space` toggles between Chinese and English | Fcitx5 compose only |
|
||||
| Arch packages | The channel's `.omarchy.cn` mirror first, its `.omarchy.org` mirror behind it, and the ArchLinuxCN repository with its keyring | `.omarchy.org` mirrors |
|
||||
| Omarchy packages | `pkgs.omarchy.org` | `pkgs.omarchy.org` |
|
||||
| Developer mirrors | npm, PyPI and uv, Go, rustup and crates.io, RubyGems and Bundler, Maven | Upstream registries |
|
||||
| Software | _Install > China Apps_ and _Setup > Claude Code API_ | Hidden |
|
||||
|
||||
Chinese text renders in the right Noto CJK variant everywhere: Simplified for Chinese, Traditional for Taiwan and Hong Kong, Japanese and Korean for those languages. That works in both regions.
|
||||
pacman tries the `.cn` mirror first. If it is unreachable or lacks a package, pacman falls through to `.org`, which works for anyone with their own VPN. The installer carries the China packages on the ISO, so a China install needs no network.
|
||||
|
||||
Every package mirror lists the `.cn` server first and the `.org` server second. If the `.cn` mirror is unreachable, or a package is missing there, pacman falls through to `.org`, which works for anyone with their own VPN, and then to the USTC Arch mirror. The ArchLinuxCN community repository is enabled too.
|
||||
## Chinese input
|
||||
|
||||
## Input
|
||||
The keyboard screen comes right after the language. After choosing Chinese it opens on _Chinese (Simplified, Pinyin)_; keep it and Fcitx5 Pinyin is ready at first login. `Ctrl + Shift + Space` switches Chinese input on and off. Add or change an input language later under _Setup > Input Language_. See [Keyboard, Mouse, Trackpad](34-keyboard-mouse-trackpad.md).
|
||||
|
||||
The installer sets up Rime Ice offline, from data bundled on the ISO, so Chinese input works the moment you log in. Rime is the active input method and `Ctrl + Space` switches to plain US keyboard input and back. The input method is installed by [rime-ice-installer](https://github.com/manateelazycat/rime-ice-installer); run `rime-ice-installer` again any time to adjust it.
|
||||
Chinese text renders in the right Noto CJK variant everywhere: Simplified for Chinese, Traditional for Taiwan and Hong Kong, Japanese and Korean for those languages.
|
||||
|
||||
## Developer mirrors
|
||||
|
||||
@@ -32,10 +36,12 @@ _Install > China Apps_ installs WeChat, QQ, Feishu, DingTalk, Tencent Meeting an
|
||||
|
||||
_Setup > Claude Code API_ points Claude Code at DeepSeek, Kimi Code, GLM, MiniMax or any Anthropic-compatible endpoint. Pick a provider, paste its API key and confirm the model. The answers land in the `env` block of `~/.claude/settings.json`. Kimi Code (`kimi`) and Dim Agent (`dim`) are preinstalled in every region; see [AI](17-ai.md).
|
||||
|
||||
## Switching regions
|
||||
## Switching later
|
||||
|
||||
_Setup > Region_ switches an installed machine either way, or run `omarchy region cn` or `omarchy region global` from a terminal. Switching to China first runs a full system update, so the input method installs against current packages, then installs the input method packages, records the region, moves the package mirrors to `.cn`, adds ArchLinuxCN and downloads Rime Ice. If the packages fail to install, the machine stays Global; if the Rime Ice download fails, China stays selected and running the switch again finishes it. The language and the developer mirrors follow at the next login, so the switch then offers to log out at once.
|
||||
_Setup > Region_, or `omarchy region cn` and `omarchy region global`, switches the region. China runs a full system update, trusts the ArchLinuxCN keyring, records the region, moves pacman to the `.cn` mirrors and links the developer mirrors. Global moves pacman back to `.org`, removes the developer mirrors and the Claude Code API setup, and hides _Install > China Apps_; apps you installed from it stay. The developer mirrors change at the next login, so the switch offers to log out.
|
||||
|
||||
Switching to Global moves packages back to the `.org` servers, the language back to English, and removes the developer mirrors, China Apps and the Claude Code API setup. It keeps the input method and the CJK fonts, so you can still type and read Chinese. `omarchy update` never changes the region or the language; only these switches do.
|
||||
_Setup > Language_, or `omarchy language zh` and `omarchy language en`, switches the system language. The menus and the desktop shell follow at the next login, so it offers to log out too.
|
||||
|
||||
A machine prepared for another owner keeps the region picked in the installer, and a factory reset returns a machine to the region it was installed with. Neither carries the input method's offline data, so pick _Setup > Region > China_ once the machine is online; that setup downloads Rime Ice from GitHub.
|
||||
Neither switch touches the other, the input methods or the CJK fonts. `omarchy update` never changes the region or the language.
|
||||
|
||||
A machine prepared for another owner keeps the language picked in the installer but installs Global, since its timezone is only set on first boot. Switch it under _Setup > Region_ once it is online.
|
||||
@@ -0,0 +1,26 @@
|
||||
echo "Re-stage the active theme with packaged intro videos"
|
||||
|
||||
theme_name_path="$HOME/.local/state/omarchy/current/theme.name"
|
||||
|
||||
[[ -s $theme_name_path ]] || exit 0
|
||||
|
||||
theme_name=$(<"$theme_name_path")
|
||||
[[ $theme_name =~ ^[[:alnum:]_][[:alnum:].+_-]*$ ]] || exit 0
|
||||
[[ -d $OMARCHY_PATH/themes/$theme_name/backgrounds/intros ]] || exit 0
|
||||
|
||||
omarchy-theme-refresh
|
||||
|
||||
state_dir="$HOME/.local/state/omarchy"
|
||||
marker="$state_dir/background-intro.session-id"
|
||||
session_id=${OMARCHY_SESSION_ID:-${HYPRLAND_INSTANCE_SIGNATURE:-}}
|
||||
if [[ -z $session_id ]]; then
|
||||
hypr_dir=$(find "${XDG_RUNTIME_DIR:-/run/user/$UID}/hypr" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -n 1 | cut -d' ' -f2- || true)
|
||||
session_id=${hypr_dir##*/}
|
||||
fi
|
||||
[[ -n $session_id ]] || exit 0
|
||||
mkdir -p "$state_dir"
|
||||
marker_staged=$(mktemp "$state_dir/.background-intro.session-id.XXXXXX")
|
||||
trap 'rm -f "$marker_staged"' EXIT
|
||||
printf '%s\n' "$session_id" >"$marker_staged"
|
||||
mv -f "$marker_staged" "$marker"
|
||||
trap - EXIT
|
||||
@@ -0,0 +1,15 @@
|
||||
echo "Enable overlay scrollbars in Chromium-based browsers"
|
||||
|
||||
for flags_file in "$HOME"/.config/{chromium,chrome,microsoft-edge-stable,brave,brave-origin}-flags.conf; do
|
||||
[[ -f $flags_file ]] || continue
|
||||
grep -Eq -- '^--enable-features=([^,]+,)*OverlayScrollbar([,:]|$)' "$flags_file" && continue
|
||||
|
||||
if grep -q -- '^--enable-features=' "$flags_file"; then
|
||||
sed -i --follow-symlinks \
|
||||
-e '/^--enable-features=/ s/$/,OverlayScrollbar/' \
|
||||
-e '/^--enable-features=/ s/=,/=/' "$flags_file"
|
||||
else
|
||||
[[ -n $(tail -c1 "$flags_file") ]] && echo >>"$flags_file"
|
||||
echo '--enable-features=OverlayScrollbar' >>"$flags_file"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,28 @@
|
||||
echo "Prioritize the Omarchy package repository over Arch repositories"
|
||||
|
||||
# Move the existing section rather than replacing pacman.conf so channel URLs,
|
||||
# custom repositories, and administrator options survive the migration.
|
||||
pacman_config=$(mktemp)
|
||||
trap 'rm -f "$pacman_config"' EXIT
|
||||
|
||||
awk '
|
||||
/^[[:space:]]*\[[^]]+\]/ {
|
||||
section = $0
|
||||
sub(/^[[:space:]]*\[/, "", section)
|
||||
sub(/\].*$/, "", section)
|
||||
if (section != "options") repositories = 1
|
||||
}
|
||||
!repositories { prefix = prefix $0 "\n"; next }
|
||||
section == "omarchy" { omarchy = omarchy $0 "\n"; next }
|
||||
{ others = others $0 "\n" }
|
||||
END { printf "%s%s%s", prefix, omarchy, others }
|
||||
' /etc/pacman.conf >"$pacman_config"
|
||||
|
||||
if ! cmp -s /etc/pacman.conf "$pacman_config"; then
|
||||
sudo cp -a /etc/pacman.conf /etc/pacman.conf.bak
|
||||
sudo install -m 644 -o root -g root "$pacman_config" /etc/pacman.conf
|
||||
fi
|
||||
|
||||
# Refresh databases and upgrade against the new repository priority. Run this
|
||||
# even when the config is already ordered so a failed upgrade can be retried.
|
||||
omarchy-update-system-pkgs
|
||||
@@ -0,0 +1,9 @@
|
||||
echo "Preserve Voxtype as the default for existing dictation users"
|
||||
|
||||
backend_config="${XDG_CONFIG_HOME:-$HOME/.config}/omarchy/defaults/dictation"
|
||||
if [[ ! -e $backend_config ]] && omarchy-pkg-present voxtype-bin; then
|
||||
mkdir -p "${backend_config%/*}"
|
||||
temporary=$(mktemp "$backend_config.XXXXXX")
|
||||
printf '%s\n' voxtype > "$temporary"
|
||||
mv -f "$temporary" "$backend_config"
|
||||
fi
|
||||
+21
-22
@@ -23,11 +23,9 @@ These replace the 2026-10-05 decisions where they disagree.
|
||||
|
||||
## Shipping prerequisites
|
||||
|
||||
- The region screen, Rime bundling and local `rime-ice-installer` build exist only on the `omarchycn` branches: a China-ready ISO is built by running `./bin/omarchy-iso-make --local-source ../omarchy ../omarchy-pkgs` from `omarchy-iso/` until they are merged and published.
|
||||
- omarchy-pkgs PR 265 (rime-ice-installer) must merge, with `omarchy-offline.patch` upstreamed to manateelazycat/rime-ice-installer, before non-local ISO builds can list it.
|
||||
- omarchy-iso PR 210 (stop passing `offline` to archinstall 4.5's `sanity_check`) is cherry-picked on `omarchy-iso/omarchycn`; the edge snapshot already ships archinstall 4.5.
|
||||
- A local-source ISO builds `omarchy-dev` and `omarchy-settings-dev` under the published version number, so the next edge release would replace them with builds that lack the China code. Until these branches ship upstream, the ISO pins the local builds with `IgnorePkg`, a `pre-refresh-pacman` hook and an ALPM hook that aborts any transaction replacing them (omarchy-iso README). Pinned machines get no Omarchy updates.
|
||||
- `stable-mirror.omarchy.cn`, `rc-mirror.omarchy.cn` and `mirror.omarchy.cn` must resolve for Arch packages to come from China; until then pacman falls through to `.org`.
|
||||
- The region profile and timezone mapping wait on omarchy #13690 and omarchy-iso #241; input methods on omarchy #14433 (omarchy-iso #237 is merged). The language screen, CJK console, Chinese UI, developer mirrors, China apps and Claude Code API exist only on the `omarchycn` branches: build a test ISO with `./bin/omarchy-iso-make --local-source ../omarchy ../omarchy-pkgs` from `omarchy-iso/`.
|
||||
- A local-source ISO builds `omarchy-dev` and `omarchy-settings-dev` under the published version number, so the next edge release would replace them with builds that lack this code. Until these branches ship upstream, the ISO pins the local builds with `IgnorePkg`, a `pre-refresh-pacman` hook and an ALPM hook that aborts any transaction replacing them (omarchy-iso README). Pinned machines get no Omarchy updates.
|
||||
- `stable-mirror.omarchy.cn`, `rc-mirror.omarchy.cn` and `mirror.omarchy.cn` resolve and serve `core.db` (checked 2026-10-09).
|
||||
|
||||
## Tasks
|
||||
|
||||
@@ -56,26 +54,27 @@ These replace the 2026-10-05 decisions where they disagree.
|
||||
- [x] T21 Split language from region: `omarchy-language en|zh` and Setup > Language own `LANG`; `omarchy-region` and `install/config/region.sh` stop touching the locale
|
||||
- [x] T22 omarchy-pkgs: revert the `rime-ice-installer` package
|
||||
- [x] T23 omarchy-iso: merge upstream `quattro` (#237) and #241; remove `--region`, `prepare-region.sh`, the region skel, the region preselected timezone, Rime bundling and its pin
|
||||
- [ ] T24 omarchy-iso: the language screen replaces the region screen; Chinese reruns in cage + foot with `noto-fonts-cjk` unpacked from the offline repository (no font packages added to the ISO); the CJK console lists only US-keymap keyboards and preselects Chinese (Simplified, Pinyin); the target gets `zh_CN.UTF-8` through `locale_config`; the harness takes `--language zh`
|
||||
- [ ] T25 Docs: `AGENTS.md`, `docs/regions.md`, `manual/52-china.md`, omarchy-iso README
|
||||
- [ ] T26 Build the local-source ISO and test in QEMU: Chinese + `Asia/Shanghai`, English + `Europe/Berlin`, runtime region and language switches
|
||||
- [ ] T27 Acceptance checklist for T19–T26
|
||||
- [x] T24 omarchy-iso: the language screen replaces the region screen; Chinese reruns in cage + foot with `noto-fonts-cjk` unpacked from the offline repository (no font packages added to the ISO); the CJK console lists only US-keymap keyboards and preselects Chinese (Simplified, Pinyin); the target gets `zh_CN.UTF-8` through `locale_config`; the harness takes `--language zh`
|
||||
- [x] T25 Docs: `AGENTS.md`, `docs/regions.md`, `manual/52-china.md`, omarchy-iso README
|
||||
- [x] T26 Build the local-source ISO and test in QEMU: Chinese + `Asia/Shanghai`, English + `America/New_York`, runtime region and language switches
|
||||
- [x] T27 Acceptance checklist for T19–T26
|
||||
|
||||
## Acceptance checklist
|
||||
|
||||
Evidence comes from the T12 runs of `omarchy-iso/release/omarchy-2026.10.06-x86_64-local.iso` in QEMU (omarchy 4e323342, omarchy-iso 8d069b0, omarchy-pkgs a84ad64c).
|
||||
Evidence comes from the T26 runs of `omarchy-iso/release/omarchy-2026.10.09-x86_64-local.iso` (omarchy fcd681c0, omarchy-iso 30f63b6, omarchy-pkgs a4cc3234): run A in host QEMU with the guest cut off from the network during install, run B in the ISO test harness.
|
||||
|
||||
| # | Requirement | Result | Evidence |
|
||||
| # | Requirement (discussion of 2026-10-08) | Result | Evidence |
|
||||
|---|---|---|---|
|
||||
| 1 | China package sources: `pkgs.omarchy.cn` before `.org`, Arch `.cn` mirrors before `.org`, `omarchy update` keeps them | Pass | China install has a `.cn` twin above each `.org` server in `pacman.conf` and `mirrorlist`; `omarchy update -y` keeps them while the installed Omarchy package is the local build (see the pin above); `region-test.sh`, `region-switch-test.sh`. Arch `.cn` hosts do not resolve yet, so pacman falls through to `.org`. |
|
||||
| 2 | Region choice on the installer's first screen | Pass | Configurator asks Global/China before the keyboard; summary shows the region; `test_region.py`, `setup-form-test.sh`; both QEMU installs answered it. |
|
||||
| 3 | Chinese input ready at first login, Ctrl+Space toggles | Pass | China install: Rime Ice and the Wanxiang model from the ISO, no prompt; after reboot Ctrl+Space typed 中文, then Ctrl+Space typed `ok`. Global install has no Rime. |
|
||||
| 4 | CJK font fix (PR 9693) | Pass | `50-omarchy.conf` rebuilt; `fontconfig-cjk-test.sh` covers fc-match and the string lang path apps use; China VM resolves SC, JP and Latin mono as expected. |
|
||||
| 5 | Claude Code third-party API in settings; Kimi Code and Dim | Pass | Setup > Claude Code API (China only) wrote the DeepSeek env; `claude -p` reached api.deepseek.com (401 for a fake key). Both regions install the `dim` and `kimi` launchers; in China `dim` 0.5.16 and `kimi` 2.1.1 ran. |
|
||||
| 6 | China mirrors for npm, Arch, Ruby, Go, Rust, Python, Java | Pass | China VM: npm, pip, uv, go, rustup, cargo, gem, bundler and maven each exit 0 and leave the artifact; eight log an npmmirror or Aliyun URL, cargo logs the `aliyun` registry and caches under `mirrors.aliyun.com-*`. |
|
||||
| 7 | China software entry | Pass | Install > China Apps lists WeChat and the others only in China; `china-apps-test.sh`. |
|
||||
| 8 | Region switch in settings | Pass | China to Global: `.org` only, `en_US`, drop-ins and Claude env removed, CJK fonts and Rime kept, China rows hidden, `omarchy update -y` stays Global and English (old bug fixed). Global to China: `.cn` twins, `zh_CN`, Rime set up, update keeps it. Global from the start installs no Rime. Dim and Kimi stay in every case. |
|
||||
| 9 | Stock Omarchy logo only | Pass | No OmarchyCN asset in any of the three branches; screens show the stock logo. |
|
||||
| - | Test ISO built and tested in QEMU | Pass | Local-source ISO; China guest checks 16/16, Global 9/9; in-guest `./test/all`: 4635 passing assertions, run exited 1 (`ascii-test` fails only without `LANG` over ssh; the guest has no `omarchy-pkgs` checkout). |
|
||||
| 1 | Language picker on the first screen; Chinese gets cage + foot | Pass | Run A: the VT screen offers English and Chinese; Chinese reruns in cage + foot on a bilingual language screen, and every later screen and the install dashboard read in Chinese. |
|
||||
| 2 | CJK font from the offline repository, not added to the ISO | Pass | `unpack_cjk_font` extracts `NotoSansCJK-Regular.ttc` from the offline mirror's `noto-fonts-cjk`; the ISO package list drops `ttf-jetbrains-mono` and `wqy-microhei` and keeps only `cage` and `foot`. |
|
||||
| 3 | Translated installer strings and the target locale | Pass | Run A: summary, finish screen and dashboard in Chinese; installed `LANG=zh_CN.UTF-8` through `locale_config`. Run B: English throughout, `LANG=en_US.UTF-8`. |
|
||||
| 4 | Region from the timezone, no extra step (#13690, #241) | Pass | Run A: `Asia/Shanghai` shows 区域 cn on the summary and writes `/etc/omarchy/region` = `cn`. Run B: `America/New_York` installs Global with no marker. |
|
||||
| 5 | Offline install ready without network | Pass | Run A installed in 8m13s with the guest network cut off; ArchLinuxCN keyring trusted, `mirror.omarchy.cn` first, Pinyin ready at first login. |
|
||||
| 6 | `.cn` pacman mirrors from #13690; ours dropped | Pass | China: `mirror.omarchy.cn` before `mirror.omarchy.org`, `[archlinuxcn]` from USTC. Global: mirrorlist equals the channel template; `pacman.conf` differs only by the local-build `IgnorePkg` pin. |
|
||||
| 7 | Developer mirrors on the cn profile | Pass | China links `/etc/environment.d/90-omarchy-china.conf`, `/.cargo/config.toml`, `/etc/gemrc`; Global removes them and `NPM_CONFIG_REGISTRY` is gone after relogin. |
|
||||
| 8 | Input methods from dhh's #14433; Rime removed | Pass | Chinese preselects Chinese (Simplified, Pinyin); `INPUT_METHOD=pinyin`; `Ctrl + Shift + Space` then `nihao` typed 你好. No Rime package, asset or pin remains. |
|
||||
| 9 | Region and language switch separately at runtime | Pass | `omarchy region global` and `cn` leave `LANG` alone; `omarchy language en` and `zh` leave the region alone; each offers logout in the right language; Claude Code API and China Apps follow the region. |
|
||||
| 10 | `omarchy update` keeps region and language | Pass | After `omarchy update -y`: `LANG=zh_CN.UTF-8`, region `cn`, `mirror.omarchy.cn` first, `[archlinuxcn]` present. |
|
||||
| - | Automated tests | Pass, no new regressions | omarchy shell suite in an Arch container: every failure is environmental and predates these tasks; some sit in branch-only tests (`fontconfig-cjk-test` needs the Omarchy font set, `hyprland-jis-bindings-test` needs `lua`, `menu-plugin-test` and `tailscale-receive-test` chmod a symlink on the read-only mount). omarchy-iso `./test/all` passes apart from `test_keyboard` (needs systemd's `localectl`). Harness acceptance: four failures outside this branch (Mozc Qt; weather and reminder prompts on screen but missed by OCR; `linux-omarchy` vs `omarchy-bore` kernel). |
|
||||
|
||||
Open items outside this branch: the shipping prerequisites above; upstream acceptance-suite failures (surfaces kept mapped at 1x1, English OCR on a Chinese UI, notification OCR timeout); deferred provisioning and factory reset get no offline input method; the menu's "Go…" prompt and keybinding and notification texts stay English.
|
||||
Open items outside this branch: the shipping prerequisites above; the four acceptance failures listed; `.automated_script.sh` exits 1 when its dashboard returns without rebooting after the mirror warm-up has finished, from upstream's `EXIT` trap under `set -e`; each `omarchy region` switch asks for the sudo password several times through refresh's command-scoped `sudo` calls.
|
||||
@@ -1,5 +1,6 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import qs.Commons as Commons
|
||||
import "BorderGeometry.js" as Geometry
|
||||
|
||||
// Central border-spec factory for shell surfaces and controls. A spec carries
|
||||
@@ -22,7 +23,7 @@ QtObject {
|
||||
}
|
||||
|
||||
function value(section, key) {
|
||||
var v = Color.shellValues[section + "." + key]
|
||||
var v = Commons.Color.shellValues[section + "." + key]
|
||||
return (v === undefined || v === null) ? "" : v
|
||||
}
|
||||
|
||||
@@ -39,7 +40,7 @@ QtObject {
|
||||
var seen = {}
|
||||
while (s.match(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/) && !seen[s]) {
|
||||
seen[s] = true
|
||||
var next = Color.shellValues[s]
|
||||
var next = Commons.Color.shellValues[s]
|
||||
if (next === undefined || next === null || String(next).length === 0) break
|
||||
s = String(next).replace(/^\s+|\s+$/g, "")
|
||||
}
|
||||
@@ -68,10 +69,10 @@ QtObject {
|
||||
|
||||
var s = String(color || "").replace(/^\s+|\s+$/g, "")
|
||||
var role = s.toLowerCase()
|
||||
if (role === "foreground" || role === "text") return cssColor(Color.foreground, a)
|
||||
if (role === "accent") return cssColor(Color.accent, a)
|
||||
if (role === "urgent") return cssColor(Color.urgent, a)
|
||||
if (role === "background") return cssColor(Color.background, a)
|
||||
if (role === "foreground" || role === "text") return cssColor(Commons.Color.foreground, a)
|
||||
if (role === "accent") return cssColor(Commons.Color.accent, a)
|
||||
if (role === "urgent") return cssColor(Commons.Color.urgent, a)
|
||||
if (role === "background") return cssColor(Commons.Color.background, a)
|
||||
if (role === "transparent") return "transparent"
|
||||
return Geometry.canonicalColor(s, a)
|
||||
}
|
||||
|
||||
@@ -396,7 +396,6 @@ var table = {
|
||||
"Pin": "固定",
|
||||
"Show": "显示",
|
||||
"Hide": "隐藏",
|
||||
"Dictate": "语音输入",
|
||||
"Allow Notifications": "允许通知",
|
||||
"Silence Notifications": "静默通知",
|
||||
"Day Light": "日间光线",
|
||||
@@ -434,11 +433,9 @@ var table = {
|
||||
"Download failed": "下载失败",
|
||||
"Editing config file": "正在编辑配置文件",
|
||||
"Extended mode restored": "已恢复扩展模式",
|
||||
"FIDO2 authfile needs attention": "FIDO2 认证文件需要处理",
|
||||
"Failed to start Windows VM": "无法启动 Windows 虚拟机",
|
||||
"Herdr theme sync disabled": "Herdr 主题同步已关闭",
|
||||
"Herdr theme sync enabled": "Herdr 主题同步已开启",
|
||||
"Hold F9 to dictate (or toggle with Super + Ctrl + X).": "按住 F9 语音输入(或用 Super + Ctrl + X 切换)。",
|
||||
"Laptop display disabled": "笔记本屏幕已关闭",
|
||||
"Laptop display enabled": "笔记本屏幕已开启",
|
||||
"Logging out": "正在注销",
|
||||
@@ -514,8 +511,6 @@ var table = {
|
||||
"Enable sudo and unlocking with your fingerprint.": "用指纹进行 sudo 和解锁。",
|
||||
"Set your default agent": "设置默认 AI 代理",
|
||||
"Let your favorite agent help with Omarchy.": "让你喜欢的 AI 代理帮你使用 Omarchy。",
|
||||
"Install Dictation with Voxtype": "安装 Voxtype 语音输入",
|
||||
"Click to install voice dictation for Omarchy.": "点击为 Omarchy 安装语音输入。",
|
||||
"All %1 accounts are over %2%": "所有 %1 账户都超过 %2%",
|
||||
"Staying on %1. %2 resets in %3.": "继续使用 %1。%2 将在 %3 后重置。",
|
||||
"Staying on %1.": "继续使用 %1。",
|
||||
@@ -616,5 +611,10 @@ var table = {
|
||||
"Log out now to switch the language?": "立即注销以切换语言?",
|
||||
"Log back in to switch the language.": "重新登录后切换语言。",
|
||||
"Log out now to switch the developer mirrors?": "立即注销以切换开发工具镜像?",
|
||||
"Log back in to switch the developer mirrors.": "重新登录后切换开发工具镜像。"
|
||||
"Log back in to switch the developer mirrors.": "重新登录后切换开发工具镜像。",
|
||||
"Superwhisper Dictation Ready": "Superwhisper 语音输入就绪",
|
||||
"Hold Right Alt or F9 to dictate (or toggle with Super + Ctrl + X).": "按住右 Alt 或 F9 说话(或用 Super + Ctrl + X 切换)。",
|
||||
"Background Intros": "背景开场动画",
|
||||
"Enabled": "已开启",
|
||||
"Disabled": "已关闭"
|
||||
}
|
||||
+22
-21
@@ -1,5 +1,6 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import qs.Commons as Commons
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
@@ -114,27 +115,27 @@ QtObject {
|
||||
}
|
||||
|
||||
function resolveStateColor(token, foreground, accent, urgent, fallback) {
|
||||
var fb = fallback || foreground || Color.foreground
|
||||
var fb = fallback || foreground || Commons.Color.foreground
|
||||
var s = String(token || "").replace(/^\s+|\s+$/g, "")
|
||||
var role = s.toLowerCase()
|
||||
if (role === "foreground" || role === "text") return foreground || Color.foreground
|
||||
if (role === "accent") return accent || Color.accent
|
||||
if (role === "urgent") return urgent || Color.urgent
|
||||
if (role === "background") return Color.background
|
||||
if (role === "foreground" || role === "text") return foreground || Commons.Color.foreground
|
||||
if (role === "accent") return accent || Commons.Color.accent
|
||||
if (role === "urgent") return urgent || Commons.Color.urgent
|
||||
if (role === "background") return Commons.Color.background
|
||||
if (role === "transparent") return Qt.rgba(0, 0, 0, 0)
|
||||
return colorFromHex(s, fb)
|
||||
}
|
||||
|
||||
function normalStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(normalColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
return resolveStateColor(normalColorToken, foreground, accent, urgent, foreground || Commons.Color.foreground)
|
||||
}
|
||||
|
||||
function hoverStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(hoverColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
return resolveStateColor(hoverColorToken, foreground, accent, urgent, foreground || Commons.Color.foreground)
|
||||
}
|
||||
|
||||
function selectedStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(selectedColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
return resolveStateColor(selectedColorToken, foreground, accent, urgent, foreground || Commons.Color.foreground)
|
||||
}
|
||||
|
||||
function pressedStateColor(foreground, accent, urgent) {
|
||||
@@ -149,7 +150,7 @@ QtObject {
|
||||
}
|
||||
|
||||
function selectionStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(selectionColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
return resolveStateColor(selectionColorToken, foreground, accent, urgent, foreground || Commons.Color.foreground)
|
||||
}
|
||||
|
||||
function normalFillFor(foreground, accent, urgent) { return Util.alpha(normalStateColor(foreground, accent, urgent), normalFillAlpha) }
|
||||
@@ -187,17 +188,17 @@ QtObject {
|
||||
}
|
||||
|
||||
// Convenience colors resolved against the foundational palette.
|
||||
readonly property color normalFill: normalFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color hoverFill: hoverFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color selectedFill: selectedFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color pressedFill: pressedFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color focusFillColor: focusFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color normalBorderColor: normalBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color hoverBorderColor: hoverBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color selectedBorderColor: selectedBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color focusBorderColor: focusBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color selectedAccentFill: Util.alpha(Color.accent, selectedFillAlpha)
|
||||
readonly property color selectionFill: selectionFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color normalFill: normalFillFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color hoverFill: hoverFillFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color selectedFill: selectedFillFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color pressedFill: pressedFillFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color focusFillColor: focusFillFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color normalBorderColor: normalBorderFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color hoverBorderColor: hoverBorderFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color selectedBorderColor: selectedBorderFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color focusBorderColor: focusBorderFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
readonly property color selectedAccentFill: Util.alpha(Commons.Color.accent, selectedFillAlpha)
|
||||
readonly property color selectionFill: selectionFillFor(Commons.Color.foreground, Commons.Color.accent, Commons.Color.urgent)
|
||||
|
||||
// ---------------------------------------------------------- spacing
|
||||
//
|
||||
@@ -390,7 +391,7 @@ QtObject {
|
||||
}
|
||||
|
||||
// Pull typography, bar dimensions, state tokens, and spacing out of the
|
||||
// shell.toml dict that Color already parsed. Called by Color.loadShell so
|
||||
// shell.toml dict that Color already parsed. Called by Commons.Color.loadShell so
|
||||
// a single parse pass feeds both singletons.
|
||||
function applyShellValues(values) {
|
||||
var fontOut = {}
|
||||
|
||||
+7
-6
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// The button. One component for every clickable thing in the kit.
|
||||
// States compose independently and are applied in priority order:
|
||||
@@ -32,9 +33,9 @@ BorderSurface {
|
||||
property bool bordered: false
|
||||
|
||||
// Colors. Defaults track the theme; per-instance overrides are honored.
|
||||
property color foreground: Color.foreground
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color background: "transparent"
|
||||
property color accent: Color.accent
|
||||
property color accent: Commons.Color.accent
|
||||
|
||||
// Sizing.
|
||||
property string fontFamily: Style.font.family
|
||||
@@ -54,9 +55,9 @@ BorderSurface {
|
||||
// Tooltip palette. Auto-rendered if tooltipText is set. Defaults pull
|
||||
// from [tooltip] in shell.toml; override per-instance only when a button
|
||||
// intentionally wants a tooltip that diverges from the theme.
|
||||
property color tooltipBackground: Color.tooltip.background
|
||||
property color tooltipForeground: Color.tooltip.text
|
||||
property color tooltipBorder: Color.tooltip.border
|
||||
property color tooltipBackground: Commons.Color.tooltip.background
|
||||
property color tooltipForeground: Commons.Color.tooltip.text
|
||||
property color tooltipBorder: Commons.Color.tooltip.border
|
||||
|
||||
signal clicked()
|
||||
signal rightClicked()
|
||||
@@ -77,7 +78,7 @@ BorderSurface {
|
||||
readonly property bool hot: mouseArea.containsMouse || hasCursor
|
||||
readonly property bool _showFocusRing: focusable && activeFocus
|
||||
readonly property color _selectedColor: Style.selectedStateColor(root.foreground, root.accent)
|
||||
readonly property var _tooltipBorderSpec: Border.localOrSurfaceSpec("tooltip", "border", root.tooltipBorder, Color.tooltip.border, Math.max(1, Style.normalBorderWidth))
|
||||
readonly property var _tooltipBorderSpec: Border.localOrSurfaceSpec("tooltip", "border", root.tooltipBorder, Commons.Color.tooltip.border, Math.max(1, Style.normalBorderWidth))
|
||||
readonly property var _focusBorderSpec: Border.controlSpec("focus", root.foreground, root.accent)
|
||||
readonly property var _hoverBorderSpec: Border.controlSpec("hover-cursor", root.foreground, root.accent)
|
||||
readonly property var _selectedBorderSpec: Border.controlSpec("selected", root.foreground, root.accent)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Mutually-exclusive row of Buttons — the form-style "pick one of N"
|
||||
// pattern (bar position top/right/bottom/left, theme preset chips, etc.).
|
||||
@@ -25,9 +26,9 @@ Row {
|
||||
|
||||
property var options: []
|
||||
property string value: ""
|
||||
property color foreground: Color.foreground
|
||||
property color background: Color.background
|
||||
property color accent: Color.accent
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color background: Commons.Color.background
|
||||
property color accent: Commons.Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.body
|
||||
property bool focusable: true
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -9,11 +10,11 @@ Item {
|
||||
property string cancelText: I18n.tr("Cancel")
|
||||
property string confirmText: I18n.tr("Confirm")
|
||||
property int selectedIndex: 1
|
||||
property color background: Color.background
|
||||
property color foreground: Color.foreground
|
||||
property color scrim: Util.alpha(Color.background, 0.7)
|
||||
property color selectedBackground: Util.alpha(Color.foreground, 0.08)
|
||||
property color selectedText: Color.accent
|
||||
property color background: Commons.Color.background
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color scrim: Util.alpha(Commons.Color.background, 0.7)
|
||||
property color selectedBackground: Util.alpha(Commons.Color.foreground, 0.08)
|
||||
property color selectedText: Commons.Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property int cornerRadius: Style.cornerRadius
|
||||
|
||||
@@ -98,10 +99,10 @@ Item {
|
||||
width: Style.space(88)
|
||||
height: Style.space(34)
|
||||
color: selected
|
||||
? (destructive ? Util.alpha(Color.urgent, 0.22) : root.selectedBackground)
|
||||
? (destructive ? Util.alpha(Commons.Color.urgent, 0.22) : root.selectedBackground)
|
||||
: "transparent"
|
||||
borderSpec: Border.flat(destructive
|
||||
? (selected ? Color.urgent : Util.alpha(Color.urgent, 0.56))
|
||||
? (selected ? Commons.Color.urgent : Util.alpha(Commons.Color.urgent, 0.56))
|
||||
: (selected ? root.selectedText : Util.alpha(root.foreground, 0.38)), Style.normalBorderWidth)
|
||||
radius: 0
|
||||
|
||||
@@ -109,7 +110,7 @@ Item {
|
||||
textFormat: Text.PlainText
|
||||
anchors.centerIn: parent
|
||||
text: modelData
|
||||
color: destructive ? (selected ? Color.urgent : root.foreground) : (selected ? root.selectedText : root.foreground)
|
||||
color: destructive ? (selected ? Commons.Color.urgent : root.foreground) : (selected ? root.selectedText : root.foreground)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Shared visual chrome for keyboard-and-mouse-navigable items inside a panel.
|
||||
// Contract: items must NOT read `containsMouse` for color/border. Mouse
|
||||
@@ -19,8 +20,8 @@ BorderSurface {
|
||||
property bool outline: false
|
||||
property bool bordered: false
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color accent: Color.accent
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color accent: Commons.Color.accent
|
||||
property color fill: Style.hoverFillFor(foreground, accent)
|
||||
property color currentFill: Style.selectedFillFor(foreground, accent)
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Themed single-select dropdown. Trigger row paints with the kit's focus
|
||||
// chrome; the popup anchors below and uses Color.popups.background +
|
||||
// Color.popups.border so it reads as a panel surface rather than the
|
||||
// chrome; the popup anchors below and uses Commons.Color.popups.background +
|
||||
// Commons.Color.popups.border so it reads as a panel surface rather than the
|
||||
// platform-native ComboBox look.
|
||||
//
|
||||
// `options` accepts either a plain string[] or an array of
|
||||
@@ -22,11 +23,11 @@ Item {
|
||||
property string value: ""
|
||||
property var options: []
|
||||
|
||||
property color foreground: Color.popups.text
|
||||
property color background: Color.popups.background
|
||||
property color popupBorder: Color.popups.border
|
||||
property color accent: Color.accent
|
||||
readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Color.popups.border, Style.normalBorderWidth)
|
||||
property color foreground: Commons.Color.popups.text
|
||||
property color background: Commons.Color.popups.background
|
||||
property color popupBorder: Commons.Color.popups.border
|
||||
property color accent: Commons.Color.accent
|
||||
readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Commons.Color.popups.border, Style.normalBorderWidth)
|
||||
property string fontFamily: Style.font.family
|
||||
property int rowHeight: Style.spacing.controlHeight
|
||||
property int popupRowHeight: Style.spacing.popupRowHeight
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Layer-shell popup attached to a bar widget icon, designed for
|
||||
// click-driven AND keyboard-driven panels (e.g. SUPER+CTRL+W summon).
|
||||
@@ -44,7 +45,7 @@ PanelWindow {
|
||||
property int padding: Style.spacing.popupPadding
|
||||
property int contentWidth: Style.space(280)
|
||||
property int contentHeight: Style.space(200)
|
||||
property var borderSpec: Border.surfaceSpec("popups", "border", Color.popups.border, Math.max(1, Style.space(2)))
|
||||
property var borderSpec: Border.surfaceSpec("popups", "border", Commons.Color.popups.border, Math.max(1, Style.space(2)))
|
||||
property bool centerOnBar: false
|
||||
property bool open: false
|
||||
property int gap: Style.gapsOut // distance between bar edge and panel
|
||||
@@ -382,7 +383,7 @@ PanelWindow {
|
||||
y: root.cardOrigin.y
|
||||
width: root.contentWidth
|
||||
height: root.contentHeight
|
||||
color: Color.popups.background
|
||||
color: Commons.Color.popups.background
|
||||
borderSpec: root.borderSpec
|
||||
padding: root.padding
|
||||
radius: Style.cornerRadius
|
||||
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Controls as QQC
|
||||
import QtQuick.Window
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Searchable multi-select dropdown. Trigger shape matches Dropdown /
|
||||
// SearchableDropdown; the popup shows a search field, an optional refresh
|
||||
@@ -33,11 +34,11 @@ Item {
|
||||
property string triggerLabel: ""
|
||||
property bool showLabel: true
|
||||
|
||||
property color foreground: Color.popups.text
|
||||
property color background: Color.popups.background
|
||||
property color popupBorder: Color.popups.border
|
||||
property color accent: Color.accent
|
||||
readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Color.popups.border, Style.normalBorderWidth)
|
||||
property color foreground: Commons.Color.popups.text
|
||||
property color background: Commons.Color.popups.background
|
||||
property color popupBorder: Commons.Color.popups.border
|
||||
property color accent: Commons.Color.accent
|
||||
readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Commons.Color.popups.border, Style.normalBorderWidth)
|
||||
property string fontFamily: Style.font.family
|
||||
property int rowHeight: Style.spacing.controlHeight
|
||||
property int popupRowHeight: Style.spacing.popupRowHeight
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Column {
|
||||
id: root
|
||||
@@ -10,8 +11,8 @@ Column {
|
||||
property int from: 0
|
||||
property int to: 100
|
||||
property int stepSize: 1
|
||||
property color foreground: Color.foreground
|
||||
property color accent: Color.accent
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color accent: Commons.Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.body
|
||||
property real fieldWidth: Style.spacing.numberFieldWidth
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -7,7 +8,7 @@ Item {
|
||||
property string text: ""
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.body
|
||||
property color color: Color.foreground
|
||||
property color color: Commons.Color.foreground
|
||||
property bool debugBounds: false
|
||||
|
||||
readonly property int renderedFontSize: Math.max(1, Math.round(fontSize))
|
||||
|
||||
+57
-24
@@ -3,12 +3,9 @@ import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
|
||||
// A fullscreen overlay whose surface outlives each open. A fresh surface draws
|
||||
// its first frames before the compositor sends its fractional scale, so an
|
||||
// overlay mapped per open flashed blurry until the scale arrived. Closed, the
|
||||
// surface parks as a 1x1, input-less, content-less layer below windows: small
|
||||
// enough to cost nothing, off the overlay layer so it never blocks direct
|
||||
// scanout for fullscreen apps. Opening only resizes and raises it.
|
||||
// Map a fresh fullscreen surface on each open. Keep its first frames
|
||||
// transparent until Qt receives the output's scale, so it opens sharp without
|
||||
// retaining a parked surface that can be closed when a monitor disconnects.
|
||||
PanelWindow {
|
||||
id: window
|
||||
|
||||
@@ -17,11 +14,31 @@ PanelWindow {
|
||||
property int shownLayer: WlrLayer.Overlay
|
||||
property int shownKeyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
|
||||
// The surface no longer lands on the focused output by being mapped there,
|
||||
// so it follows the focused monitor each time it is shown. Unset until the
|
||||
// first show lets the compositor choose.
|
||||
property var targetScreen: null
|
||||
property Region emptyRegion: Region {}
|
||||
readonly property var targetMonitor: {
|
||||
var monitors = Hyprland.monitors.values
|
||||
for (var i = 0; i < monitors.length; i++) {
|
||||
if (targetScreen && monitors[i].name === targetScreen.name) return monitors[i]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Wait for fullscreen geometry as well as scale so no smaller opening frame
|
||||
// is stretched across the output. Fractional scale uses units of 1/120;
|
||||
// compare in those units to tolerate floating point rounding.
|
||||
readonly property bool contentReady: shown && backingWindowVisible && !!targetScreen
|
||||
&& width === targetScreen.width && height === targetScreen.height
|
||||
&& !!targetMonitor && Math.round(devicePixelRatio * 120) === Math.round(targetMonitor.scale * 120)
|
||||
property bool contentRevealed: false
|
||||
|
||||
onContentReadyChanged: {
|
||||
contentRevealed = false
|
||||
// Resize and scale notifications arrive while Qt is still updating the
|
||||
// window. Reveal on the next turn, once its content layout has caught up.
|
||||
if (contentReady) Qt.callLater(function() {
|
||||
if (window.contentReady) window.contentRevealed = true
|
||||
})
|
||||
}
|
||||
|
||||
function focusedScreen() {
|
||||
var monitor = Hyprland.focusedMonitor
|
||||
@@ -29,28 +46,44 @@ PanelWindow {
|
||||
for (var i = 0; i < Quickshell.screens.length; i++) {
|
||||
if (Quickshell.screens[i].name === name) return Quickshell.screens[i]
|
||||
}
|
||||
return null
|
||||
return Quickshell.screens.length > 0 ? Quickshell.screens[0] : null
|
||||
}
|
||||
|
||||
onShownChanged: if (shown) targetScreen = focusedScreen() || targetScreen
|
||||
// Live monitor scaling does not emit a Hyprland monitor event, so its cached
|
||||
// scale can lag behind Qt. Refresh on opening and on scale changes while open.
|
||||
onShownChanged: {
|
||||
targetScreen = shown ? focusedScreen() : null
|
||||
if (shown) Hyprland.refreshMonitors()
|
||||
}
|
||||
onDevicePixelRatioChanged: if (shown) Hyprland.refreshMonitors()
|
||||
|
||||
visible: true
|
||||
Connections {
|
||||
target: Quickshell
|
||||
function onScreensChanged() {
|
||||
if (window.shown && Quickshell.screens.indexOf(window.targetScreen) < 0) {
|
||||
window.targetScreen = null
|
||||
// Let the removed output finish closing its old window before mapping
|
||||
// a replacement, rather than recreating it during Qt's teardown.
|
||||
Qt.callLater(function() {
|
||||
if (window.shown && !window.targetScreen) window.targetScreen = window.focusedScreen()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visible: shown && !!targetScreen && !!targetMonitor
|
||||
screen: targetScreen
|
||||
anchors { top: true; left: true; bottom: shown; right: shown }
|
||||
implicitWidth: 1
|
||||
implicitHeight: 1
|
||||
mask: shown ? null : emptyRegion
|
||||
anchors { top: true; left: true; bottom: true; right: true }
|
||||
color: "transparent"
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: shown ? shownLayer : WlrLayer.Bottom
|
||||
WlrLayershell.keyboardFocus: shown ? shownKeyboardFocus : WlrKeyboardFocus.None
|
||||
WlrLayershell.layer: shownLayer
|
||||
WlrLayershell.keyboardFocus: shownKeyboardFocus
|
||||
|
||||
// Draw nothing until the surface has actually grown. A frame drawn while it
|
||||
// is still 1x1 holds only the scrim's color, which the compositor would
|
||||
// stretch across the whole screen until the fullscreen frame arrives.
|
||||
// Opacity preserves keyboard handling while the scale arrives, including
|
||||
// search keystrokes and Escape pressed immediately after opening.
|
||||
Binding {
|
||||
target: window.contentItem
|
||||
property: "visible"
|
||||
value: window.shown && window.width > 1 && window.height > 1
|
||||
property: "opacity"
|
||||
value: window.contentRevealed ? 1 : 0
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Base item for plugin popup widgets. Many first-party plugins expose a bar
|
||||
// button plus a popup from one QML entry point; this base owns the shared
|
||||
@@ -19,7 +20,7 @@ Item {
|
||||
property bool popoutSwitchClosing: false
|
||||
|
||||
readonly property bool opened: panelController.open
|
||||
readonly property color barForeground: bar ? bar.barForeground : Color.foreground
|
||||
readonly property color barForeground: bar ? bar.barForeground : Commons.Color.foreground
|
||||
|
||||
function open() { panelController.show() }
|
||||
function close() { panelController.hide() }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Small (22×22 by default) icon button used at the right edge of panel rows
|
||||
// for inline actions — forget network, confirm passphrase, unpair device,
|
||||
@@ -29,7 +30,7 @@ BorderSurface {
|
||||
|
||||
property string iconText: ""
|
||||
property string tooltipText: ""
|
||||
property color foreground: Color.foreground
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color hoverColor: foreground
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.icon
|
||||
@@ -57,7 +58,7 @@ BorderSurface {
|
||||
? Border.controlSpec("focus", hoverColor, hoverColor)
|
||||
: (_hot && bordered
|
||||
? Border.controlSpec("hover-cursor", hoverColor, hoverColor)
|
||||
: (bordered ? Border.controlSpec("normal", foreground, Color.accent) : Border.none()))
|
||||
: (bordered ? Border.controlSpec("normal", foreground, Commons.Color.accent) : Border.none()))
|
||||
|
||||
color: _showFocusRing
|
||||
? Style.focusFillFor(hoverColor, hoverColor)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -8,7 +9,7 @@ Item {
|
||||
property string title: ""
|
||||
property string meta: ""
|
||||
property string detail: ""
|
||||
property color foreground: Color.foreground
|
||||
property color foreground: Commons.Color.foreground
|
||||
property string fontFamily: Style.font.family
|
||||
property real iconSize: Style.font.display
|
||||
property real iconOpacity: 1.0
|
||||
@@ -71,7 +72,7 @@ Item {
|
||||
implicitHeight: detailText.implicitHeight + Style.space(4)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "transparent"
|
||||
borderSpec: Border.controlSpec("normal", root.foreground, Color.accent)
|
||||
borderSpec: Border.controlSpec("normal", root.foreground, Commons.Color.accent)
|
||||
radius: Style.cornerRadius
|
||||
|
||||
Text {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Small-caps-style label that introduces a panel section ("DNS provider",
|
||||
// "Wi-Fi networks", "Output device", "Paired devices"). Sits between a
|
||||
@@ -7,7 +8,7 @@ import qs.Commons
|
||||
Text {
|
||||
id: root
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color foreground: Commons.Color.foreground
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.caption
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// 1px horizontal divider for panel sections. The alpha-on-foreground tint
|
||||
// keeps the rule legible against the panel background without competing
|
||||
@@ -7,7 +8,7 @@ import qs.Commons
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color foreground: Commons.Color.foreground
|
||||
property real strength: 0.12
|
||||
|
||||
width: parent ? parent.width : implicitWidth
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -10,9 +11,9 @@ Item {
|
||||
property real maximum: 1
|
||||
property real step: 0.05
|
||||
property bool integer: false
|
||||
property color trackColor: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "#333"
|
||||
property color fillColor: bar ? bar.foreground : Color.foreground
|
||||
property color knobColor: bar ? bar.foreground : Color.foreground
|
||||
property color trackColor: bar ? Style.selectedFillFor(bar.foreground, Commons.Color.accent) : "#333"
|
||||
property color fillColor: bar ? bar.foreground : Commons.Color.foreground
|
||||
property color knobColor: bar ? bar.foreground : Commons.Color.foreground
|
||||
property bool dragging: false
|
||||
property real trackHeight: Math.max(4, Math.round(Style.spacing.controlHeight * 0.11))
|
||||
property real knobSize: Math.max(14, Math.round(Style.spacing.controlHeight * 0.38))
|
||||
@@ -23,7 +24,7 @@ Item {
|
||||
// crossing the track shows). Purely visual — snapping is the caller's job via
|
||||
// `integer`/`step` or an index-based value. Default 0 leaves the track plain.
|
||||
property int tickCount: 0
|
||||
property color tickColor: bar ? bar.background : Color.background
|
||||
property color tickColor: bar ? bar.background : Commons.Color.background
|
||||
|
||||
onValueChanged: if (!dragging) liveValue = value
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Styled wrapper around Qt Quick Controls ToolTip. Drop-in: declare inside
|
||||
// the hovered item and bind `visible` to the hover state, e.g.
|
||||
@@ -9,7 +10,7 @@ import qs.Commons
|
||||
// text: "Forget network"
|
||||
// }
|
||||
//
|
||||
// Defaults pull from [tooltip] in shell.toml via Color.tooltip.*. Override
|
||||
// Defaults pull from [tooltip] in shell.toml via Commons.Color.tooltip.*. Override
|
||||
// the panel* properties per-instance only when you need a tooltip that
|
||||
// intentionally diverges from the theme.
|
||||
//
|
||||
@@ -18,13 +19,13 @@ import qs.Commons
|
||||
ToolTip {
|
||||
id: root
|
||||
|
||||
property color panelForeground: Color.tooltip.text
|
||||
property color panelBackground: Color.tooltip.background
|
||||
property color panelBorder: Color.tooltip.border
|
||||
property color panelForeground: Commons.Color.tooltip.text
|
||||
property color panelBackground: Commons.Color.tooltip.background
|
||||
property color panelBorder: Commons.Color.tooltip.border
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.bodySmall
|
||||
|
||||
readonly property var panelBorderSpec: Border.localOrSurfaceSpec("tooltip", "border", panelBorder, Color.tooltip.border, Style.normalBorderWidth)
|
||||
readonly property var panelBorderSpec: Border.localOrSurfaceSpec("tooltip", "border", panelBorder, Commons.Color.tooltip.border, Style.normalBorderWidth)
|
||||
|
||||
delay: 400
|
||||
padding: 0
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
PopupWindow {
|
||||
id: root
|
||||
@@ -13,8 +14,8 @@ PopupWindow {
|
||||
property int padding: Style.spacing.popupPadding
|
||||
property int contentWidth: Style.space(280)
|
||||
property int contentHeight: Style.space(200)
|
||||
property color borderColor: Color.popups.border
|
||||
property var borderSpec: Border.localOrSurfaceSpec("popups", "border", borderColor, Color.popups.border, Math.max(1, Style.space(2)))
|
||||
property color borderColor: Commons.Color.popups.border
|
||||
property var borderSpec: Border.localOrSurfaceSpec("popups", "border", borderColor, Commons.Color.popups.border, Math.max(1, Style.space(2)))
|
||||
property bool open: false
|
||||
property bool centerOnBar: false
|
||||
// "click" — uses HyprlandFocusGrab so clicking outside dismisses the popup.
|
||||
@@ -150,7 +151,7 @@ PopupWindow {
|
||||
BorderSurface {
|
||||
id: card
|
||||
anchors.fill: parent
|
||||
color: Color.popups.background
|
||||
color: Commons.Color.popups.background
|
||||
borderSpec: root.borderSpec
|
||||
padding: root.padding
|
||||
radius: Style.cornerRadius
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Searchable single-select dropdown. Same trigger shape as Dropdown, but
|
||||
// the popup leads with an embedded TextField that filters the option
|
||||
@@ -25,11 +26,11 @@ Item {
|
||||
property string emptyText: I18n.tr("No matches")
|
||||
property string triggerLabel: ""
|
||||
|
||||
property color foreground: Color.popups.text
|
||||
property color background: Color.popups.background
|
||||
property color popupBorder: Color.popups.border
|
||||
property color accent: Color.accent
|
||||
readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Color.popups.border, Style.normalBorderWidth)
|
||||
property color foreground: Commons.Color.popups.text
|
||||
property color background: Commons.Color.popups.background
|
||||
property color popupBorder: Commons.Color.popups.border
|
||||
property color accent: Commons.Color.accent
|
||||
readonly property var popupBorderSpec: Border.localOrSurfaceSpec("popups", "border", popupBorder, Commons.Color.popups.border, Style.normalBorderWidth)
|
||||
property string fontFamily: Style.font.family
|
||||
property int rowHeight: Style.spacing.controlHeight
|
||||
property int popupRowHeight: Style.spacing.popupRowHeight
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick.Shapes
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
// Centered speed test overlay shared by the network and disk speed tests. No
|
||||
@@ -288,7 +289,7 @@ PanelWindow {
|
||||
// round caps would leave a stray dot at the foot of the scale.
|
||||
ShapePath {
|
||||
strokeWidth: dial.arcWidth * 3
|
||||
strokeColor: dial.arcVisible ? Qt.rgba(Color.accent.r, Color.accent.g, Color.accent.b, 0.18) : "transparent"
|
||||
strokeColor: dial.arcVisible ? Qt.rgba(Commons.Color.accent.r, Commons.Color.accent.g, Commons.Color.accent.b, 0.18) : "transparent"
|
||||
fillColor: "transparent"
|
||||
capStyle: ShapePath.RoundCap
|
||||
|
||||
@@ -305,7 +306,7 @@ PanelWindow {
|
||||
// Value: fills behind the needle.
|
||||
ShapePath {
|
||||
strokeWidth: dial.arcWidth
|
||||
strokeColor: dial.arcVisible ? Color.accent : "transparent"
|
||||
strokeColor: dial.arcVisible ? Commons.Color.accent : "transparent"
|
||||
fillColor: "transparent"
|
||||
capStyle: ShapePath.RoundCap
|
||||
|
||||
@@ -356,8 +357,8 @@ PanelWindow {
|
||||
radius: width / 2
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: Color.accent }
|
||||
GradientStop { position: 0.55; color: Color.accent }
|
||||
GradientStop { position: 0.0; color: Commons.Color.accent }
|
||||
GradientStop { position: 0.55; color: Commons.Color.accent }
|
||||
GradientStop { position: 1.0; color: "transparent" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Single-line text input with the kit's focus + selection styling. Inherits
|
||||
// from Qt Quick Controls TextField so the underlying type's API (text,
|
||||
@@ -18,8 +19,8 @@ import qs.Commons
|
||||
TextField {
|
||||
id: root
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color accent: Color.accent
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color accent: Commons.Color.accent
|
||||
property color selectionTint: Style.selectionFillFor(foreground, accent)
|
||||
property bool password: false
|
||||
property real horizontalPadding: Style.spacing.controlPaddingX
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Labeled toggle row: title + optional description on the left, a
|
||||
// `ToggleSwitch` on the right. Clicking anywhere on the row emits `clicked()`;
|
||||
@@ -28,8 +29,8 @@ BorderSurface {
|
||||
// Override per-instance if a caller wants the opposite.
|
||||
property bool rounded: Style.cornerRadius > 0
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color accent: Color.accent
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color accent: Commons.Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property real titleSize: Style.font.subtitle
|
||||
property real descriptionSize: Style.font.caption
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Bare on/off switch: a track with a sliding knob and no label. This is the
|
||||
// switch `Toggle` parks at the end of its labeled row, factored out so panel
|
||||
@@ -40,8 +41,8 @@ Item {
|
||||
property bool cursorRing: interactive
|
||||
property int cursorPad: Style.space(6)
|
||||
property bool rounded: Style.cornerRadius > 0
|
||||
property color foreground: Color.foreground
|
||||
property color accent: Color.accent
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color accent: Commons.Color.accent
|
||||
|
||||
signal toggled()
|
||||
signal hovered(bool isHovered)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -8,8 +9,8 @@ Item {
|
||||
property string text: ""
|
||||
property string fontFamily: bar ? bar.fontFamily : Style.font.family
|
||||
property real fontSize: Style.font.body
|
||||
property color foreground: bar ? bar.barForeground : Color.foreground
|
||||
property color activeColor: bar ? bar.urgent : Color.urgent
|
||||
property color foreground: bar ? bar.barForeground : Commons.Color.foreground
|
||||
property color activeColor: bar ? bar.urgent : Commons.Color.urgent
|
||||
property bool active: false
|
||||
property real horizontalMargin: 8.5
|
||||
property real verticalPadding: 6
|
||||
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
Panel {
|
||||
@@ -11,11 +12,11 @@ Panel {
|
||||
ipcTarget: "omarchy.agents"
|
||||
manageIpc: false
|
||||
|
||||
readonly property color foreground: bar ? bar.foreground : Color.foreground
|
||||
readonly property color urgent: bar ? bar.urgent : Color.urgent
|
||||
readonly property color foreground: bar ? bar.foreground : Commons.Color.foreground
|
||||
readonly property color urgent: bar ? bar.urgent : Commons.Color.urgent
|
||||
readonly property color dim: Qt.darker(foreground, 1.55)
|
||||
readonly property color surface: Color.popups.background
|
||||
readonly property color track: Style.selectedFillFor(foreground, Color.accent)
|
||||
readonly property color surface: Commons.Color.popups.background
|
||||
readonly property color track: Style.selectedFillFor(foreground, Commons.Color.accent)
|
||||
readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family
|
||||
|
||||
// Every subscription on one page, limits first: the question this panel
|
||||
@@ -725,7 +726,7 @@ Panel {
|
||||
function iconCandidatesForProvider(p, surfaceColor) {
|
||||
if (!p) return []
|
||||
var candidates = []
|
||||
if (colorLuminance(surfaceColor || Color.background) >= 0.5)
|
||||
if (colorLuminance(surfaceColor || Commons.Color.background) >= 0.5)
|
||||
candidates.push(Qt.resolvedUrl("assets/" + p.providerId + "-light.svg"))
|
||||
candidates.push(Qt.resolvedUrl("assets/" + p.providerId + ".svg"))
|
||||
return candidates
|
||||
@@ -1116,7 +1117,7 @@ Panel {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
textFormat: Text.PlainText
|
||||
text: choice.modelData.providerName
|
||||
color: choice.hasCursor ? Color.accent : root.foreground
|
||||
color: choice.hasCursor ? Commons.Color.accent : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
font.bold: true
|
||||
@@ -1196,7 +1197,7 @@ Panel {
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: root.addCode
|
||||
color: Color.accent
|
||||
color: Commons.Color.accent
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.display
|
||||
font.bold: true
|
||||
@@ -1538,7 +1539,7 @@ Panel {
|
||||
property bool hasCursor: false
|
||||
implicitHeight: tileBody.implicitHeight + Style.space(20)
|
||||
radius: Style.cornerRadius
|
||||
color: hasCursor ? root.alpha(Color.accent, 0.14) : root.alpha(root.foreground, 0.05)
|
||||
color: hasCursor ? root.alpha(Commons.Color.accent, 0.14) : root.alpha(root.foreground, 0.05)
|
||||
onHasCursorChanged: if (hasCursor) root.revealItem(tile)
|
||||
|
||||
Row {
|
||||
@@ -1550,7 +1551,7 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
textFormat: Text.PlainText
|
||||
text: tile.glyph
|
||||
color: Color.accent
|
||||
color: Commons.Color.accent
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
}
|
||||
@@ -1559,7 +1560,7 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
textFormat: Text.PlainText
|
||||
text: tile.title
|
||||
color: tile.hasCursor ? Color.accent : root.foreground
|
||||
color: tile.hasCursor ? Commons.Color.accent : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
font.bold: true
|
||||
@@ -1593,16 +1594,16 @@ Panel {
|
||||
implicitHeight: implicitWidth
|
||||
radius: Style.cornerRadius
|
||||
// The cursor needs more than a shade deeper to read on a tinted square.
|
||||
color: root.alpha(Color.accent, hasCursor ? 0.3 : 0.12)
|
||||
color: root.alpha(Commons.Color.accent, hasCursor ? 0.3 : 0.12)
|
||||
border.width: hasCursor ? Math.max(1, Style.hoverBorderWidth) : 0
|
||||
border.color: Color.accent
|
||||
border.color: Commons.Color.accent
|
||||
onHasCursorChanged: if (hasCursor) root.revealItem(heroButton)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
textFormat: Text.PlainText
|
||||
text: heroButton.glyph
|
||||
color: Color.accent
|
||||
color: Commons.Color.accent
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
}
|
||||
@@ -1634,7 +1635,7 @@ Panel {
|
||||
readonly property bool hot: linkMouse.containsMouse || picked
|
||||
onPickedChanged: if (picked) root.revealItem(link)
|
||||
textFormat: Text.PlainText
|
||||
color: current ? Color.accent : (hot ? root.foreground : idleColor)
|
||||
color: current ? Commons.Color.accent : (hot ? root.foreground : idleColor)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
font.bold: current
|
||||
@@ -1715,7 +1716,7 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Math.min(implicitWidth, parent.width * 0.6)
|
||||
text: head.label
|
||||
color: head.picked ? Color.accent : root.foreground
|
||||
color: head.picked ? Commons.Color.accent : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
font.bold: head.isActive
|
||||
@@ -1821,7 +1822,7 @@ Panel {
|
||||
anchors.right: parent.right
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("ACTIVE")
|
||||
color: Color.accent
|
||||
color: Commons.Color.accent
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
font.bold: true
|
||||
|
||||
@@ -5,12 +5,14 @@ import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Shapes
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var shell: null
|
||||
property bool suspended: false
|
||||
readonly property string home: Quickshell.env("HOME")
|
||||
readonly property string stateHome: home + "/.local/state"
|
||||
readonly property string currentBackgroundLink: stateHome + "/omarchy/current/background"
|
||||
@@ -37,11 +39,20 @@ Item {
|
||||
property var sizeQueue: []
|
||||
property bool finishingTransition: false
|
||||
property int backgroundVersion: 0
|
||||
property int reloadVersion: 0
|
||||
property int revealStartedVersion: -1
|
||||
property int pendingThemeVersion: -1
|
||||
property string pendingColorsRaw: ""
|
||||
property string pendingShellRaw: ""
|
||||
property real revealProgress: 1
|
||||
readonly property bool ready: {
|
||||
if (isVideo(displayedBackground)) return true
|
||||
if (backgrounds.instances.length === 0) return false
|
||||
for (var panel of backgrounds.instances) {
|
||||
if (!panel.backgroundReady) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function isVideo(path) {
|
||||
return Util.isVideoPath(path)
|
||||
@@ -56,7 +67,8 @@ Item {
|
||||
}
|
||||
|
||||
function setBackground(path, instant) {
|
||||
transitionBackground("", path, path, instant, false)
|
||||
if (instant) reloadVersion += 1
|
||||
transitionBackground("", path, path, instant, instant)
|
||||
}
|
||||
|
||||
function transitionBackground(fromPath, path, finalPath, instant, force) {
|
||||
@@ -106,10 +118,10 @@ Item {
|
||||
// pending; the latest theme payload should still apply.
|
||||
if (pendingThemeVersion < 0) return
|
||||
pendingThemeFallbackTimer.stop()
|
||||
Color.loadColors(pendingColorsRaw)
|
||||
// Color.loadShell also refreshes Style so the type scale flips with the
|
||||
Commons.Color.loadColors(pendingColorsRaw)
|
||||
// Commons.Color.loadShell also refreshes Style so the type scale flips with the
|
||||
// background reveal instead of waiting for a separate reload path.
|
||||
Color.loadShell(pendingShellRaw)
|
||||
Commons.Color.loadShell(pendingShellRaw)
|
||||
Style.scheduleRefresh()
|
||||
pendingThemeVersion = -1
|
||||
pendingColorsRaw = ""
|
||||
@@ -223,6 +235,10 @@ Item {
|
||||
root.setBackground(path, true)
|
||||
}
|
||||
|
||||
function setSuspended(value: string): void {
|
||||
root.suspended = value === "true"
|
||||
}
|
||||
|
||||
function transition(fromPath: string, path: string): void {
|
||||
root.transitionBackground(fromPath, path, path, false, false)
|
||||
}
|
||||
@@ -259,7 +275,7 @@ Item {
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Style.duration(420)
|
||||
easing.type: Easing.InOutCubic
|
||||
easing.type: Easing.OutCubic
|
||||
onFinished: {
|
||||
if (root.incomingBackground) {
|
||||
root.displayedBackground = root.currentBackground || root.incomingBackground
|
||||
@@ -272,6 +288,7 @@ Item {
|
||||
Component.onCompleted: refreshBackground()
|
||||
|
||||
Variants {
|
||||
id: backgrounds
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
@@ -279,7 +296,7 @@ Item {
|
||||
required property var modelData
|
||||
|
||||
screen: modelData
|
||||
visible: !remapGuard.remapping
|
||||
visible: !remapGuard.remapping && !root.suspended
|
||||
anchors { top: true; bottom: true; left: true; right: true }
|
||||
|
||||
ScreenMoveRemap {
|
||||
@@ -294,6 +311,13 @@ Item {
|
||||
updatesEnabled: true
|
||||
|
||||
property bool maskReady: false
|
||||
property int readyFrames: 0
|
||||
readonly property bool backgroundReady: base.ready && readyFrames >= 2
|
||||
|
||||
FrameAnimation {
|
||||
running: base.ready && panel.readyFrames < 2
|
||||
onTriggered: panel.readyFrames += 1
|
||||
}
|
||||
|
||||
// Decode the wallpaper at the size this screen can show, not the size
|
||||
// it was shipped at. With PreserveAspectCrop Qt takes sourceSize as the
|
||||
@@ -337,9 +361,12 @@ Item {
|
||||
id: base
|
||||
anchors.fill: parent
|
||||
path: root.displayedBackground
|
||||
version: root.reloadVersion
|
||||
cached: true
|
||||
constrainDecode: true
|
||||
decodeSize: panel.decodeSize(root.displayedBackground)
|
||||
onReadyChanged: {
|
||||
panel.readyFrames = 0
|
||||
if (ready && root.finishingTransition) {
|
||||
root.incomingBackground = ""
|
||||
root.oldBackground = ""
|
||||
|
||||
+14
-13
@@ -5,6 +5,7 @@ import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
import "BarModel.js" as BarModel
|
||||
|
||||
@@ -71,18 +72,18 @@ Item {
|
||||
property string fontFamily: Style.font.family
|
||||
// Bound to the central Color singleton so the bar tracks shell.toml's
|
||||
// [bar] section. Property names kept for the rest of this file's bindings.
|
||||
property color themeForeground: Color.bar.text
|
||||
property color themeContrastForeground: Color.background
|
||||
property color transparentForeground: Color.bar.text
|
||||
property color themeForeground: Commons.Color.bar.text
|
||||
property color themeContrastForeground: Commons.Color.background
|
||||
property color transparentForeground: Commons.Color.bar.text
|
||||
property color foreground: themeForeground
|
||||
property color barForeground: useTransparentForeground ? transparentForeground : themeForeground
|
||||
property bool foregroundAnimationEnabled: true
|
||||
property color background: Color.bar.background
|
||||
property color urgent: Color.bar.active
|
||||
property color background: Commons.Color.bar.background
|
||||
property color urgent: Commons.Color.bar.active
|
||||
|
||||
Behavior on barForeground { enabled: root.foregroundAnimationEnabled; ColorAnimation { duration: Style.duration(420); easing.type: Easing.InOutCubic } }
|
||||
Behavior on background { ColorAnimation { duration: Style.duration(420); easing.type: Easing.InOutCubic } }
|
||||
Behavior on urgent { ColorAnimation { duration: Style.duration(420); easing.type: Easing.InOutCubic } }
|
||||
Behavior on barForeground { enabled: root.foregroundAnimationEnabled; ColorAnimation { duration: Style.duration(420); easing.type: Easing.OutCubic } }
|
||||
Behavior on background { ColorAnimation { duration: Style.duration(420); easing.type: Easing.OutCubic } }
|
||||
Behavior on urgent { ColorAnimation { duration: Style.duration(420); easing.type: Easing.OutCubic } }
|
||||
property var tooltipTarget: null
|
||||
property var pendingTooltipTarget: null
|
||||
property string tooltipText: ""
|
||||
@@ -1342,8 +1343,8 @@ Item {
|
||||
id: tooltipBubble
|
||||
implicitWidth: tooltipLabel.implicitWidth + 20
|
||||
implicitHeight: tooltipLabel.implicitHeight + 14
|
||||
color: Color.tooltip.background
|
||||
borderSpec: Border.surfaceSpec("tooltip", "border", Color.tooltip.border, 1)
|
||||
color: Commons.Color.tooltip.background
|
||||
borderSpec: Border.surfaceSpec("tooltip", "border", Commons.Color.tooltip.border, 1)
|
||||
radius: Style.cornerRadius
|
||||
|
||||
Text {
|
||||
@@ -1351,7 +1352,7 @@ Item {
|
||||
textFormat: Text.PlainText
|
||||
anchors.centerIn: parent
|
||||
text: root.tooltipText
|
||||
color: Color.tooltip.text
|
||||
color: Commons.Color.tooltip.text
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -1470,7 +1471,7 @@ Item {
|
||||
y: targetRect ? Math.round(targetRect.y) : 0
|
||||
width: targetRect ? targetRect.width : 0
|
||||
height: targetRect ? targetRect.height : 0
|
||||
color: Color.accent
|
||||
color: Commons.Color.accent
|
||||
radius: Math.min(width, height) / 2
|
||||
}
|
||||
}
|
||||
@@ -1905,7 +1906,7 @@ Item {
|
||||
|
||||
visible: opacity > 0
|
||||
opacity: slot.panelOpen && !slot.dragSource ? 0.9 : 0
|
||||
color: Color.accent
|
||||
color: Commons.Color.accent
|
||||
radius: Math.min(width, height) / 2
|
||||
width: root.vertical ? Style.space(2) : slot.panelIndicatorExtent
|
||||
height: root.vertical ? slot.panelIndicatorExtent : Style.space(2)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
id: root
|
||||
|
||||
property string state: "idle"
|
||||
property string icon: ""
|
||||
|
||||
active: state === "recording"
|
||||
activeText: icon
|
||||
inactiveText: ""
|
||||
activeTooltipText: state
|
||||
inactiveTooltipText: I18n.tr("Dictate")
|
||||
|
||||
function update(raw) {
|
||||
var data = extractData(raw)
|
||||
|
||||
state = String(data.alt || data.class || "idle")
|
||||
if (state === "recording") icon = ""
|
||||
else if (state === "transcribing") icon = ""
|
||||
else icon = ""
|
||||
}
|
||||
|
||||
Process {
|
||||
command: ["bash", "-c", "omarchy-voxtype-status"]
|
||||
running: true
|
||||
stdout: SplitParser {
|
||||
onRead: function(data) { root.update(data) }
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: function() {
|
||||
if (!root.bar) return
|
||||
root.bar.run("omarchy-voxtype-config")
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
@@ -16,7 +17,7 @@ BarIndicator {
|
||||
activeTooltipText: I18n.tr("Disable Passwordless Sudo")
|
||||
inactiveTooltipText: I18n.tr("Passwordless Sudo")
|
||||
useActiveColor: true
|
||||
activeColor: Color.urgent
|
||||
activeColor: Commons.Color.urgent
|
||||
|
||||
function refresh() {
|
||||
if (!root.bar) return
|
||||
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
@@ -34,7 +35,7 @@ BarWidget {
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
text: root.title
|
||||
color: root.bar ? root.bar.barForeground : Color.foreground
|
||||
color: root.bar ? root.bar.barForeground : Commons.Color.foreground
|
||||
font.family: root.bar ? root.bar.fontFamily : Style.font.family
|
||||
font.pixelSize: Style.font.body
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -31,11 +31,6 @@
|
||||
"label": "Passwordless sudo",
|
||||
"description": "Warning while passwordless root access is enabled"
|
||||
},
|
||||
{
|
||||
"value": "Dictation",
|
||||
"label": "Dictation",
|
||||
"description": "Voice typing status"
|
||||
},
|
||||
{
|
||||
"value": "ScreenRecording",
|
||||
"label": "Screen recording",
|
||||
|
||||
@@ -8,7 +8,7 @@ BarWidget {
|
||||
id: root
|
||||
moduleName: "omarchy.indicators"
|
||||
|
||||
readonly property var defaultIndicatorEntries: [ "PasswordlessSudo", "Dictation", "ScreenRecording", "Reminder", "NightLight", "Dnd", "StayAwake" ]
|
||||
readonly property var defaultIndicatorEntries: [ "PasswordlessSudo", "ScreenRecording", "Reminder", "NightLight", "Dnd", "StayAwake" ]
|
||||
readonly property var indicatorEntries: indicatorEntriesFromSettings(settings)
|
||||
property var activeIndicatorIds: []
|
||||
property var indicatorActiveStates: ({})
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
import "TrayModel.js" as TrayModel
|
||||
|
||||
@@ -16,7 +17,7 @@ BarWidget {
|
||||
property bool trayMenuOpen: false
|
||||
property var activeTrayItem: null
|
||||
property var activeTrayAnchor: null
|
||||
readonly property color foreground: bar ? bar.foreground : Color.foreground
|
||||
readonly property color foreground: bar ? bar.foreground : Commons.Color.foreground
|
||||
readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family
|
||||
readonly property var pinnedIds: settings.pinned instanceof Array ? settings.pinned : []
|
||||
readonly property var hiddenIds: settings.hidden instanceof Array ? settings.hidden : []
|
||||
@@ -620,7 +621,7 @@ BarWidget {
|
||||
anchors.rightMargin: Style.space(10)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 1
|
||||
color: Color.popups.border
|
||||
color: Commons.Color.popups.border
|
||||
opacity: 0.45
|
||||
}
|
||||
}
|
||||
@@ -674,7 +675,7 @@ BarWidget {
|
||||
anchors.rightMargin: Style.space(10)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 1
|
||||
color: Color.popups.border
|
||||
color: Commons.Color.popups.border
|
||||
opacity: 0.45
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
import "ClipboardHistory.js" as ClipboardHistory
|
||||
|
||||
@@ -22,13 +23,13 @@ Item {
|
||||
// Shares the [menu] surface tokens — themes that style the menu also
|
||||
// style the clipboard. Selected-row colors composed in the
|
||||
// singleton so consumers drop them straight into Rectangle bindings.
|
||||
property color background: Color.menu.background
|
||||
property color foreground: Color.menu.text
|
||||
property color border: Color.menu.border
|
||||
property color background: Commons.Color.menu.background
|
||||
property color foreground: Commons.Color.menu.text
|
||||
property color border: Commons.Color.menu.border
|
||||
property var borderSpec: Border.surfaceSpec("menu", "border", border, Math.max(1, Style.space(2)))
|
||||
property color scrim: Color.menu.scrim
|
||||
property color selectedBackground: Color.menu.selectedBackground
|
||||
property color selectedText: Color.menu.selectedText
|
||||
property color scrim: Commons.Color.menu.scrim
|
||||
property color selectedBackground: Commons.Color.menu.selectedBackground
|
||||
property color selectedText: Commons.Color.menu.selectedText
|
||||
readonly property int cornerRadius: Style.cornerRadius
|
||||
property string fontFamily: Style.font.menuFamily
|
||||
property int contentMargin: Style.spacing.panelPadding
|
||||
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
|
||||
// Visual reference + live playground for omarchy-shell's common UI
|
||||
// components. Summon with `omarchy dev ui-preview`, or directly via:
|
||||
@@ -66,10 +67,10 @@ Item {
|
||||
property var shell: null
|
||||
|
||||
// ---- theme --------------------------------------------------------------
|
||||
readonly property color foreground: Color.foreground
|
||||
readonly property color background: Color.background
|
||||
readonly property color accent: Color.accent
|
||||
readonly property color urgent: Color.urgent
|
||||
readonly property color foreground: Commons.Color.foreground
|
||||
readonly property color background: Commons.Color.background
|
||||
readonly property color accent: Commons.Color.accent
|
||||
readonly property color urgent: Commons.Color.urgent
|
||||
readonly property string fontFamily: "monospace"
|
||||
|
||||
// Fake `bar` for components that take a whole bar object (e.g. Slider).
|
||||
|
||||
@@ -3,6 +3,7 @@ import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
import "EmojiSearch.js" as EmojiSearch
|
||||
|
||||
@@ -23,13 +24,13 @@ Item {
|
||||
// Shares the [menu] surface tokens — themes that style the menu also
|
||||
// style emojis. Selected-cell colors composed in the
|
||||
// singleton so consumers drop them straight into Rectangle bindings.
|
||||
property color background: Color.menu.background
|
||||
property color foreground: Color.menu.text
|
||||
property color border: Color.menu.border
|
||||
property color background: Commons.Color.menu.background
|
||||
property color foreground: Commons.Color.menu.text
|
||||
property color border: Commons.Color.menu.border
|
||||
property var borderSpec: Border.surfaceSpec("menu", "border", border, Math.max(1, Style.space(2)))
|
||||
property color scrim: Color.menu.scrim
|
||||
property color selectedBackground: Color.menu.selectedBackground
|
||||
property color selectedText: Color.menu.selectedText
|
||||
property color scrim: Commons.Color.menu.scrim
|
||||
property color selectedBackground: Commons.Color.menu.selectedBackground
|
||||
property color selectedText: Commons.Color.menu.selectedText
|
||||
readonly property int cornerRadius: Style.cornerRadius
|
||||
property string fontFamily: Style.font.menuFamily
|
||||
property int contentMargin: Style.spacing.panelPadding
|
||||
|
||||
@@ -5,6 +5,7 @@ import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Shapes
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
import "ImagePickerModel.js" as ImagePickerModel
|
||||
|
||||
@@ -37,14 +38,14 @@ Item {
|
||||
property string themeRows: ""
|
||||
property bool themeMode: false
|
||||
property bool themeOpenPending: false
|
||||
// Bound to the central [image-picker] section in shell.toml via Color.qml.
|
||||
// Bound to the central [image-picker] section in shell.toml via Commons.Color.qml.
|
||||
// `dimColor` tints unselected slices and text outlines on top of the scrim;
|
||||
// it intentionally tracks the foundational background, not a surface role.
|
||||
property color dimColor: Color.background
|
||||
property color foreground: Color.imagePicker.text
|
||||
property color scrim: Color.imagePicker.scrim
|
||||
property color selectedBorder: Color.imagePicker.selectedBorder
|
||||
property color unselectedBorder: Color.imagePicker.unselectedBorder
|
||||
property color dimColor: Commons.Color.background
|
||||
property color foreground: Commons.Color.imagePicker.text
|
||||
property color scrim: Commons.Color.imagePicker.scrim
|
||||
property color selectedBorder: Commons.Color.imagePicker.selectedBorder
|
||||
property color unselectedBorder: Commons.Color.imagePicker.unselectedBorder
|
||||
property int expandedWidth: 768
|
||||
property int expandedHeight: 475
|
||||
property int sliceWidth: 108
|
||||
@@ -274,7 +275,29 @@ Item {
|
||||
property var imageArray: []
|
||||
readonly property var matchingImageIndices: ImagePickerModel.matchingIndices(imageArray, filterText)
|
||||
onMatchingImageIndicesChanged: updateVisibleItems()
|
||||
onSelectedIndexChanged: updateVisibleItems()
|
||||
onSelectedIndexChanged: {
|
||||
updateVisibleItems()
|
||||
introPrepareTimer.restart()
|
||||
}
|
||||
onThemeModeChanged: if (themeMode) introPrepareTimer.restart()
|
||||
|
||||
Timer {
|
||||
id: introPrepareTimer
|
||||
interval: 75
|
||||
onTriggered: {
|
||||
if (root.opened && root.themeMode && !introPrepare.running) {
|
||||
introPrepare.theme = root.nameForPath(root.currentPath())
|
||||
introPrepare.command = ["omarchy-theme-bg-boot-intro", "--prepare-theme", root.nameForPath(root.currentPath())]
|
||||
introPrepare.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: introPrepare
|
||||
property string theme: ""
|
||||
onExited: if (root.opened && root.themeMode && theme !== root.nameForPath(root.currentPath())) introPrepareTimer.restart()
|
||||
}
|
||||
|
||||
function updateVisibleItems() {
|
||||
ImagePickerModel.syncWindow(visibleImages, ImagePickerModel.visibleWindow(matchingImageIndices, selectedIndex, previewRadius))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import qs.Ui
|
||||
|
||||
Item {
|
||||
@@ -42,8 +43,8 @@ Item {
|
||||
readonly property bool showPasswordCursor: inputEnabled && !authenticatingPassword && failureMessage.length === 0
|
||||
readonly property bool errorState: failureMessage.length > 0
|
||||
readonly property var inputBorderSpec: errorState
|
||||
? Border.surfaceSpec("lock", "border-error", Color.lock.borderError, root.outlineThickness, "border-alpha")
|
||||
: Border.surfaceSpec("lock", "border-active", Color.lock.borderActive, root.outlineThickness, "border-alpha")
|
||||
? Border.surfaceSpec("lock", "border-error", Commons.Color.lock.borderError, root.outlineThickness, "border-alpha")
|
||||
: Border.surfaceSpec("lock", "border-active", Commons.Color.lock.borderActive, root.outlineThickness, "border-alpha")
|
||||
|
||||
readonly property bool video: Util.isVideoPath(root.backgroundPath)
|
||||
readonly property bool feedActive: root.video && root.loadBackground && !root.displaysBlank && !root.powerSaverActive
|
||||
@@ -97,7 +98,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.background
|
||||
color: Commons.Color.background
|
||||
|
||||
BackgroundMedia {
|
||||
id: wallpaper
|
||||
@@ -156,7 +157,7 @@ Item {
|
||||
width: root.fieldWidth
|
||||
height: root.fieldHeight
|
||||
anchors.centerIn: parent
|
||||
color: Color.lock.background
|
||||
color: Commons.Color.lock.background
|
||||
borderSpec: root.inputBorderSpec
|
||||
radius: Style.cornerRadius
|
||||
clip: true
|
||||
@@ -179,16 +180,16 @@ Item {
|
||||
echoMode: TextInput.Password
|
||||
passwordCharacter: "\u25CF"
|
||||
passwordMaskDelay: 0
|
||||
color: Color.lock.text
|
||||
selectionColor: Color.lock.selection
|
||||
selectedTextColor: Color.lock.text
|
||||
color: Commons.Color.lock.text
|
||||
selectionColor: Commons.Color.lock.selection
|
||||
selectedTextColor: Commons.Color.lock.text
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: text.length > 0 ? Math.max(1, Math.floor(root.passwordDotFontSize * root.passwordDotScale)) : root.fieldFontSize
|
||||
font.letterSpacing: text.length > 0 ? root.passwordDotLetterSpacing * root.passwordDotScale : 0
|
||||
cursorVisible: activeFocus && root.showPasswordCursor && text.length > 0
|
||||
cursorDelegate: Rectangle {
|
||||
width: 2
|
||||
color: Color.lock.text
|
||||
color: Commons.Color.lock.text
|
||||
visible: passwordInput.cursorVisible
|
||||
}
|
||||
|
||||
@@ -224,7 +225,7 @@ Item {
|
||||
anchors.fill: passwordInput
|
||||
text: root.authenticatingPassword ? I18n.tr("Checking…") : (root.failureMessage.length > 0 ? root.failureMessage : root.placeholderText)
|
||||
visible: passwordInput.text.length === 0
|
||||
color: root.authenticatingPassword ? Color.lock.text : (root.failureMessage.length > 0 ? Color.lock.textError : Color.lock.placeholder)
|
||||
color: root.authenticatingPassword ? Commons.Color.lock.text : (root.failureMessage.length > 0 ? Commons.Color.lock.textError : Commons.Color.lock.placeholder)
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: root.fieldFontSize
|
||||
font.italic: !root.authenticatingPassword && root.failureMessage.length > 0
|
||||
@@ -247,7 +248,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: root.fingerprintConfigured
|
||||
text: root.fingerprintUnavailable ? "" : ""
|
||||
color: root.fingerprintUnavailable ? Color.lock.textError : Color.lock.placeholder
|
||||
color: root.fingerprintUnavailable ? Commons.Color.lock.textError : Commons.Color.lock.placeholder
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: Math.round(root.fieldFontSize * 1.1)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -266,7 +267,7 @@ Item {
|
||||
anchors.horizontalCenter: inputField.horizontalCenter
|
||||
visible: root.fingerprintConfigured && root.fingerprintUnavailable
|
||||
text: I18n.tr("Fingerprint reader unavailable")
|
||||
color: Color.lock.textError
|
||||
color: Commons.Color.lock.textError
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: Style.font.heading
|
||||
font.italic: true
|
||||
|
||||
@@ -4,6 +4,7 @@ import Quickshell.Io
|
||||
import Quickshell.Services.Pam
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Commons as Commons
|
||||
import "FingerprintModel.js" as FingerprintModel
|
||||
|
||||
Item {
|
||||
@@ -467,7 +468,7 @@ Item {
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: lockSurface
|
||||
color: Color.background
|
||||
color: Commons.Color.background
|
||||
|
||||
LockView {
|
||||
id: lockView
|
||||
|
||||
Loaded 100 of 246 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user